Browse Source

[Windows] WindowDecorations related integration tests (#15561)

* Enable IncludeAvaloniaGenerators on integartion tests app

* Implement basic TitleBarAutomationPeer

* Add WindowDecorationsTests (windows only for now)

* Implement window decoration tests on macOS

* Fix build on appium 1

* Fix some windows tests

* Extract WindowDecorationsTests into a separated collection, so it won't conflict

* Fix build

* Fix build
pull/16159/head
Max Katz 2 years ago
committed by GitHub
parent
commit
d4d322654e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      samples/IntegrationTestApp/IntegrationTestApp.csproj
  2. 18
      samples/IntegrationTestApp/MainWindow.axaml
  3. 147
      samples/IntegrationTestApp/MainWindow.axaml.cs
  4. 2
      samples/IntegrationTestApp/ShowWindowTest.axaml
  5. 22
      samples/IntegrationTestApp/ShowWindowTest.axaml.cs
  6. 8
      samples/IntegrationTestApp/TopmostWindowTest.axaml.cs
  7. 26
      src/Avalonia.Controls/Automation/Peers/TitleBarAutomationPeer.cs
  8. 5
      src/Avalonia.Controls/Chrome/TitleBar.cs
  9. 6
      tests/Avalonia.IntegrationTests.Appium/AppiumDriverEx.cs
  10. 5
      tests/Avalonia.IntegrationTests.Appium/CollectionDefinitions.cs
  11. 72
      tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs
  12. 243
      tests/Avalonia.IntegrationTests.Appium/WindowDecorationsTests.cs
  13. 19
      tests/Avalonia.IntegrationTests.Appium/WindowTests.cs
  14. 8
      tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs

2
samples/IntegrationTestApp/IntegrationTestApp.csproj

@ -4,6 +4,7 @@
<TargetFramework>$(AvsCurrentTargetFramework)</TargetFramework>
<Nullable>enable</Nullable>
<NoWarn>$(NoWarn);AVP1012</NoWarn>
<IncludeAvaloniaGenerators>true</IncludeAvaloniaGenerators>
</PropertyGroup>
<PropertyGroup>
@ -37,5 +38,6 @@
<Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\ReferenceCoreLibraries.props" />
<Import Project="..\..\build\SourceGenerators.props" />
</Project>

18
samples/IntegrationTestApp/MainWindow.axaml

@ -5,7 +5,6 @@
xmlns:integrationTestApp="using:IntegrationTestApp"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.MainWindow"
Name="MainWindow"
Icon="/Assets/icon.ico"
Title="IntegrationTestApp"
x:DataType="integrationTestApp:MainWindow">
@ -24,7 +23,7 @@
</NativeMenuItem>
</NativeMenu>
</NativeMenu.Menu>
<DockPanel>
<DockPanel Background="{DynamicResource SystemRegionBrush}">
<NativeMenuBar DockPanel.Dock="Top"/>
<StackPanel DockPanel.Dock="Bottom" Margin="4" Orientation="Horizontal">
<TextBlock Margin="0,0,4,0">WindowState:</TextBlock>
@ -178,7 +177,20 @@
</StackPanel>
</Grid>
</TabItem>
<TabItem Header="Window Decorations">
<StackPanel Spacing="4">
<CheckBox Name="WindowExtendClientAreaToDecorationsHint" Content="Extend Client Area to Decorations" />
<CheckBox Name="WindowForceSystemChrome" Content="Force SystemChrome" />
<CheckBox Name="WindowPreferSystemChrome" Content="Prefer SystemChrome" />
<CheckBox Name="WindowMacThickSystemChrome" Content="Mac Thick SystemChrome" />
<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>
<TabItem Header="Slider">
<DockPanel>
<DockPanel DockPanel.Dock="Top">

147
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -8,24 +8,25 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree;
using Microsoft.CodeAnalysis;
namespace IntegrationTestApp
{
public class MainWindow : Window
public partial class MainWindow : Window
{
public MainWindow()
{
// Set name in code behind, so source generator will ignore it.
Name = "MainWindow";
InitializeComponent();
InitializeViewMenu();
InitializeGesturesTab();
this.AttachDevTools();
var overlayPopups = this.Get<TextBlock>("AppOverlayPopups");
overlayPopups.Text = Program.OverlayPopups ? "Overlay Popups" : "Native Popups";
AppOverlayPopups.Text = Program.OverlayPopups ? "Overlay Popups" : "Native Popups";
AddHandler(Button.ClickEvent, OnButtonClick);
ListBoxItems = Enumerable.Range(0, 100).Select(x => "Item " + x).ToList();
@ -34,17 +35,11 @@ namespace IntegrationTestApp
public List<string> ListBoxItems { get; }
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void InitializeViewMenu()
{
var mainTabs = this.Get<TabControl>("MainTabs");
var viewMenu = (NativeMenuItem?)NativeMenu.GetMenu(this)?.Items[1];
foreach (var tabItem in mainTabs.Items.Cast<TabItem>())
foreach (var tabItem in MainTabs.Items.Cast<TabItem>())
{
var menuItem = new NativeMenuItem
{
@ -59,16 +54,16 @@ namespace IntegrationTestApp
}
}
private void ShowWindow()
private void OnShowWindow()
{
var sizeTextBox = this.GetControl<TextBox>("ShowWindowSize");
var modeComboBox = this.GetControl<ComboBox>("ShowWindowMode");
var locationComboBox = this.GetControl<ComboBox>("ShowWindowLocation");
var stateComboBox = this.GetControl<ComboBox>("ShowWindowState");
var sizeTextBox = ShowWindowSize;
var modeComboBox = ShowWindowMode;
var locationComboBox = ShowWindowLocation;
var stateComboBox = ShowWindowState;
var size = !string.IsNullOrWhiteSpace(sizeTextBox.Text) ? Size.Parse(sizeTextBox.Text) : (Size?)null;
var systemDecorations = this.GetControl<ComboBox>("ShowWindowSystemDecorations");
var extendClientArea = this.GetControl<CheckBox>("ShowWindowExtendClientAreaToDecorationsHint");
var canResizeCheckBox = this.GetControl<CheckBox>("ShowWindowCanResize");
var systemDecorations = ShowWindowSystemDecorations;
var extendClientArea = ShowWindowExtendClientAreaToDecorationsHint;
var canResizeCheckBox = ShowWindowCanResize;
var owner = (Window)this.GetVisualRoot()!;
var window = new ShowWindowTest
@ -113,7 +108,7 @@ namespace IntegrationTestApp
}
}
private void ShowTransparentWindow()
private void OnShowTransparentWindow()
{
// Show a background window to make sure the color behind the transparent window is
// a known color (green).
@ -155,7 +150,7 @@ namespace IntegrationTestApp
window.Show(backgroundWindow);
}
private void ShowTransparentPopup()
private void OnShowTransparentPopup()
{
var popup = new Popup
{
@ -196,7 +191,7 @@ namespace IntegrationTestApp
popup.Open();
}
private void SendToBack()
private void OnSendToBack()
{
var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
@ -206,7 +201,7 @@ namespace IntegrationTestApp
}
}
private void RestoreAll()
private void OnRestoreAll()
{
var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
@ -218,7 +213,7 @@ namespace IntegrationTestApp
}
}
private void ShowTopmostWindow()
private void OnShowTopmostWindow()
{
var mainWindow = new TopmostWindowTest("OwnerWindow") { Topmost = true, Title = "Owner Window"};
var ownedWindow = new TopmostWindowTest("OwnedWindow") { WindowStartupLocation = WindowStartupLocation.CenterOwner, Title = "Owned Window"};
@ -229,10 +224,10 @@ namespace IntegrationTestApp
private void InitializeGesturesTab()
{
var gestureBorder = this.GetControl<Border>("GestureBorder");
var gestureBorder2 = this.GetControl<Border>("GestureBorder2");
var lastGesture = this.GetControl<TextBlock>("LastGesture");
var resetGestures = this.GetControl<Button>("ResetGestures");
var gestureBorder = GestureBorder;
var gestureBorder2 = GestureBorder2;
var lastGesture = LastGesture;
var resetGestures = ResetGestures;
gestureBorder.Tapped += (_, _) => lastGesture.Text = "Tapped";
gestureBorder.DoubleTapped += (_, _) =>
@ -261,7 +256,7 @@ namespace IntegrationTestApp
private void MenuClicked(object? sender, RoutedEventArgs e)
{
var clickedMenuItemTextBlock = this.Get<TextBlock>("ClickedMenuItem");
var clickedMenuItemTextBlock = ClickedMenuItem;
clickedMenuItemTextBlock.Text = (sender as MenuItem)?.Header?.ToString();
}
@ -269,32 +264,76 @@ namespace IntegrationTestApp
{
var source = e.Source as Button;
if (source?.Name == "ComboBoxSelectionClear")
this.Get<ComboBox>("BasicComboBox").SelectedIndex = -1;
if (source?.Name == "ComboBoxSelectFirst")
this.Get<ComboBox>("BasicComboBox").SelectedIndex = 0;
if (source?.Name == "ListBoxSelectionClear")
this.Get<ListBox>("BasicListBox").SelectedIndex = -1;
if (source?.Name == "MenuClickedMenuItemReset")
this.Get<TextBlock>("ClickedMenuItem").Text = "None";
if (source?.Name == "ResetSliders")
this.Get<Slider>("HorizontalSlider").Value = 50;
if (source?.Name == "ShowTransparentWindow")
ShowTransparentWindow();
if (source?.Name == "ShowTransparentPopup")
ShowTransparentPopup();
if (source?.Name == "ShowWindow")
ShowWindow();
if (source?.Name == "SendToBack")
SendToBack();
if (source?.Name == "EnterFullscreen")
if (source?.Name == nameof(ComboBoxSelectionClear))
BasicComboBox.SelectedIndex = -1;
if (source?.Name == nameof(ComboBoxSelectFirst))
BasicComboBox.SelectedIndex = 0;
if (source?.Name == nameof(ListBoxSelectionClear))
BasicListBox.SelectedIndex = -1;
if (source?.Name == nameof(MenuClickedMenuItemReset))
ClickedMenuItem.Text = "None";
if (source?.Name == nameof(ResetSliders))
HorizontalSlider.Value = 50;
if (source?.Name == nameof(ShowTransparentWindow))
OnShowTransparentWindow();
if (source?.Name == nameof(ShowTransparentPopup))
OnShowTransparentPopup();
if (source?.Name == nameof(ShowWindow))
OnShowWindow();
if (source?.Name == nameof(SendToBack))
OnSendToBack();
if (source?.Name == nameof(EnterFullscreen))
WindowState = WindowState.FullScreen;
if (source?.Name == "ExitFullscreen")
if (source?.Name == nameof(ExitFullscreen))
WindowState = WindowState.Normal;
if (source?.Name == "RestoreAll")
RestoreAll();
if (source?.Name == "ShowTopmostWindow")
ShowTopmostWindow();
if (source?.Name == nameof(ShowTopmostWindow))
OnShowTopmostWindow();
if (source?.Name == nameof(RestoreAll))
OnRestoreAll();
if (source?.Name == nameof(ApplyWindowDecorations))
OnApplyWindowDecorations(this);
if (source?.Name == nameof(ShowNewWindowDecorations))
OnShowNewWindowDecorations();
}
private void OnApplyWindowDecorations(Window window)
{
window.ExtendClientAreaToDecorationsHint = WindowExtendClientAreaToDecorationsHint.IsChecked!.Value;
window.ExtendClientAreaTitleBarHeightHint =
int.TryParse(WindowTitleBarHeightHint.Text, out var val) ? val / DesktopScaling : -1;
window.ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.NoChrome
| (WindowForceSystemChrome.IsChecked == true ? ExtendClientAreaChromeHints.SystemChrome : 0)
| (WindowPreferSystemChrome.IsChecked == true ? ExtendClientAreaChromeHints.PreferSystemChrome : 0)
| (WindowMacThickSystemChrome.IsChecked == true ? ExtendClientAreaChromeHints.OSXThickTitleBar : 0);
AdjustOffsets(window);
window.Background = Brushes.Transparent;
window.PropertyChanged += WindowOnPropertyChanged;
void WindowOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
var window = (Window)sender!;
if (e.Property == OffScreenMarginProperty || e.Property == WindowDecorationMarginProperty)
{
AdjustOffsets(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}";
}
}
private void OnShowNewWindowDecorations()
{
var window = new ShowWindowTest();
OnApplyWindowDecorations(window);
window.Show();
}
}
}

2
samples/IntegrationTestApp/ShowWindowTest.axaml

@ -5,7 +5,7 @@
Name="SecondaryWindow"
x:DataType="Window"
Title="Show Window Test">
<integrationTestApp:MeasureBorder Name="MyBorder">
<integrationTestApp:MeasureBorder Name="MyBorder" Background="{DynamicResource SystemRegionBrush}">
<Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<Label Grid.Column="0" Grid.Row="1">Client Size</Label>
<TextBox Name="CurrentClientSize" Grid.Column="1" Grid.Row="1" IsReadOnly="True"

22
samples/IntegrationTestApp/ShowWindowTest.axaml.cs

@ -26,44 +26,38 @@ namespace IntegrationTestApp
}
}
public class ShowWindowTest : Window
public partial class ShowWindowTest : Window
{
private readonly DispatcherTimer? _timer;
private readonly TextBox? _orderTextBox;
public ShowWindowTest()
{
InitializeComponent();
DataContext = this;
PositionChanged += (s, e) => this.GetControl<TextBox>("CurrentPosition").Text = $"{Position}";
PositionChanged += (s, e) => CurrentPosition.Text = $"{Position}";
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
_orderTextBox = this.GetControl<TextBox>("CurrentOrder");
_orderTextBox = CurrentOrder;
_timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(250) };
_timer.Tick += TimerOnTick;
_timer.Start();
}
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
var scaling = PlatformImpl!.DesktopScaling;
this.GetControl<TextBox>("CurrentPosition").Text = $"{Position}";
this.GetControl<TextBox>("CurrentScreenRect").Text = $"{Screens.ScreenFromVisual(this)?.WorkingArea}";
this.GetControl<TextBox>("CurrentScaling").Text = $"{scaling}";
CurrentPosition.Text = $"{Position}";
CurrentScreenRect.Text = $"{Screens.ScreenFromVisual(this)?.WorkingArea}";
CurrentScaling.Text = $"{scaling}";
if (Owner is not null)
{
var ownerRect = this.GetControl<TextBox>("CurrentOwnerRect");
var owner = (Window)Owner;
ownerRect.Text = $"{owner.Position}, {PixelSize.FromSize(owner.FrameSize!.Value, scaling)}";
CurrentOwnerRect.Text = $"{owner.Position}, {PixelSize.FromSize(owner.FrameSize!.Value, scaling)}";
}
}

8
samples/IntegrationTestApp/TopmostWindowTest.axaml.cs

@ -5,17 +5,13 @@ using Avalonia.Markup.Xaml;
namespace IntegrationTestApp;
public class TopmostWindowTest : Window
public partial class TopmostWindowTest : Window
{
public TopmostWindowTest(string name)
{
Name = name;
InitializeComponent();
PositionChanged += (s, e) => this.GetControl<TextBox>("CurrentPosition").Text = $"{Position}";
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
PositionChanged += (s, e) => CurrentPosition.Text = $"{Position}";
}
private void Button_OnClick(object? sender, RoutedEventArgs e)

26
src/Avalonia.Controls/Automation/Peers/TitleBarAutomationPeer.cs

@ -0,0 +1,26 @@
using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Chrome;
namespace Avalonia.Controls.Automation.Peers;
internal class TitleBarAutomationPeer : ControlAutomationPeer
{
public TitleBarAutomationPeer(TitleBar owner) : base(owner)
{
}
protected override bool IsContentElementCore() => true;
protected override string GetClassNameCore()
{
return "TitleBar";
}
protected override string? GetAutomationIdCore() => base.GetAutomationIdCore() ?? "AvaloniaTitleBar";
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.TitleBar;
}
}

5
src/Avalonia.Controls/Chrome/TitleBar.cs

@ -1,4 +1,6 @@
using System;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Automation.Peers;
using Avalonia.Reactive;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
@ -94,5 +96,8 @@ namespace Avalonia.Controls.Chrome
_captionButtons?.Detach();
_captionButtons = null;
}
/// <inheritdoc />
protected override AutomationPeer OnCreateAutomationPeer() => new TitleBarAutomationPeer(this);
}
}

6
tests/Avalonia.IntegrationTests.Appium/AppiumDriverEx.cs

@ -67,6 +67,12 @@ public static class AppiumDriverEx
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)

5
tests/Avalonia.IntegrationTests.Appium/CollectionDefinitions.cs

@ -7,6 +7,11 @@ namespace Avalonia.IntegrationTests.Appium
{
}
[CollectionDefinition("WindowDecorations")]
public class WindowDecorationsCollection : ICollectionFixture<DefaultAppFixture>
{
}
[CollectionDefinition("OverlayPopups")]
public class OverlayPopupsCollection : ICollectionFixture<OverlayPopupsAppFixture>
{

72
tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs

@ -11,31 +11,64 @@ 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.FindElementsByTagName("TitleBar").FirstOrDefault();
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)
{
var titlebar = window.FindElementsByAccessibilityId("AvaloniaTitleBar")?.FirstOrDefault();
var closeButton = titlebar?.FindElementByName("Close");
var minimizeButton = titlebar?.FindElementByName("Minimize");
var maximizeButton = titlebar?.FindElementByName("Maximize");
return new(closeButton, minimizeButton, maximizeButton, null, titlebar);
}
public static string GetComboBoxValue(this AppiumWebElement element)
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
@ -68,6 +101,35 @@ namespace Avalonia.IntegrationTests.Appium
}
}
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())
{
return session.FindElementByXPath(
$"XCUIElementTypeWindow[@identifier='{identifier}']");
}
else
{
return session.FindElementByXPath($"//Window[@AutomationId='{identifier}']");
}
}
/// <summary>
/// Clicks a button which is expected to open a new window.
/// </summary>

243
tests/Avalonia.IntegrationTests.Appium/WindowDecorationsTests.cs

@ -0,0 +1,243 @@
using System;
using System.Threading;
using Xunit;
namespace Avalonia.IntegrationTests.Appium;
[Collection("WindowDecorations")]
public class WindowDecorationsTests : IDisposable
{
private readonly AppiumDriver _session;
public WindowDecorationsTests(DefaultAppFixture fixture)
{
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("Window Decorations");
tab.Click();
}
[PlatformFact(TestPlatforms.MacOS)] // TODO fix me on Windows
public void Window_Size_Should_Be_Consistent_Between_Toggles()
{
var window = _session.FindElementByAccessibilityId("MainWindow");
var original = window.Size;
// Step 1: keep extend client area to false, but adjust some value that should not have any effect.
SetParameters(false, false, false, false, 10);
ApplyToCurrentWindow();
Assert.Equal(original, window.Size);
// Step 2: enable and disable extended system chrome.
SetParameters(true, true, false, false, 20);
ApplyToCurrentWindow();
SetParameters(false, false, false, false, 20);
ApplyToCurrentWindow();
Assert.Equal(original, window.Size);
// Step 3: enable and disable extended client chrome.
SetParameters(true, false, true, false, 30);
ApplyToCurrentWindow();
SetParameters(false, false, true, false, 20);
ApplyToCurrentWindow();
Assert.Equal(original, window.Size);
}
[Fact]
public void Can_Restore_To_Non_Extended_State()
{
SetParameters(true, true, false, false, 20);
ApplyToCurrentWindow();
SetParameters(false, false, false, false, 1000);
ApplyToCurrentWindow();
var currentWindow = _session.GetCurrentSingleWindow();
var systemChrome = currentWindow.GetSystemChromeButtons();
var clientChrome = currentWindow.GetClientChromeButtons();
AssertSystemChrome(systemChrome, clientChrome, false);
var props = _session.FindElementByAccessibilityId("WindowDecorationProperties");
Assert.Equal($"0 0 False", props.Text);
}
[PlatformTheory(TestPlatforms.Windows)] // We don't have client chrome on macOS
[InlineData(-1)]
[InlineData(25)]
[InlineData(50)]
public void Should_Apply_Client_Chrome(int titleBarHeight)
{
SetParameters(true, false, true, false, titleBarHeight);
ApplyToCurrentWindow();
Thread.Sleep(500);
var currentWindow = _session.GetCurrentSingleWindow();
var systemChrome = currentWindow.GetSystemChromeButtons();
var clientChrome = currentWindow.GetClientChromeButtons();
AssertClientChrome(systemChrome, clientChrome, titleBarHeight);
var props = _session.FindElementByAccessibilityId("WindowDecorationProperties");
if (titleBarHeight > 0)
{
Assert.Equal($"0 {titleBarHeight} True", props.Text);
}
}
[Theory]
[InlineData(-1)]
[InlineData(25)]
[InlineData(50)]
public void Should_Apply_System_Chrome(int titleBarHeight)
{
SetParameters(true, true, false, false, titleBarHeight);
ApplyToCurrentWindow();
Thread.Sleep(500);
var currentWindow = _session.GetCurrentSingleWindow();
var systemChrome = currentWindow.GetSystemChromeButtons();
var clientChrome = currentWindow.GetClientChromeButtons();
AssertSystemChrome(systemChrome, clientChrome, true);
var props = _session.FindElementByAccessibilityId("WindowDecorationProperties");
if (titleBarHeight > 0)
{
Assert.Equal($"0 {titleBarHeight} True", props.Text);
}
}
[PlatformTheory(TestPlatforms.Windows)] // We don't have client chrome on macOS
[InlineData(-1)]
[InlineData(25)]
[InlineData(50)]
public void Should_Apply_Client_Chrome_On_New_Window(int titleBarHeight)
{
SetParameters(true, false, true, false, titleBarHeight);
using (ApplyOnNewWindow())
{
Thread.Sleep(500);
var secondaryWindow = _session.GetWindowById("SecondaryWindow");
var systemChrome = secondaryWindow.GetSystemChromeButtons();
var clientChrome = secondaryWindow.GetClientChromeButtons();
AssertClientChrome(systemChrome, clientChrome, titleBarHeight);
}
}
[PlatformTheory(TestPlatforms.MacOS)] // fix me, for some reason Windows doesn't return TitleBar system chrome for a child window.
[InlineData(-1)]
[InlineData(25)]
[InlineData(50)]
public void Should_Apply_System_Chrome_On_New_Window(int titleBarHeight)
{
SetParameters(true, true, false, false, titleBarHeight);
using (ApplyOnNewWindow())
{
Thread.Sleep(500);
var secondaryWindow = _session.GetWindowById("SecondaryWindow");
var systemChrome = secondaryWindow.GetSystemChromeButtons();
var clientChrome = secondaryWindow.GetClientChromeButtons();
AssertSystemChrome(systemChrome, clientChrome, true);
}
}
private void AssertClientChrome(WindowChrome systemChrome, WindowChrome clientChrome, int titleBarHeight)
{
// Ignore windows, it always reports full sized and enabled buttons and title bar. Just drawn behind.
if (!OperatingSystem.IsWindows())
{
// All system chrome buttons are hidden.
Assert.False(systemChrome.IsAnyButtonEnabled);
Assert.Equal(-1, systemChrome.MaxButtonHeight);
Assert.True(systemChrome.TitleBarHeight is -1 or 0);
}
// At least some client chrome buttons are shown.
Assert.True(clientChrome.IsAnyButtonEnabled);
Assert.True(clientChrome.MaxButtonHeight > 0);
if (titleBarHeight != -1)
{
Assert.Equal(titleBarHeight, clientChrome.TitleBarHeight);
}
}
private void AssertSystemChrome(WindowChrome systemChrome, WindowChrome clientChrome, bool isExtended)
{
// At least some server chrome buttons are shown.
Assert.True(systemChrome.IsAnyButtonEnabled);
Assert.True(systemChrome.MaxButtonHeight > 0);
// All client chrome buttons are hidden.
Assert.False(clientChrome.IsAnyButtonEnabled);
Assert.Equal(-1, clientChrome.MaxButtonHeight);
// System chrome is always 0px height, when client area is extended.
if (isExtended)
{
Assert.True(systemChrome.TitleBarHeight is -1 or 0);
}
// Can't get titlebar height on macOS.
else if (!OperatingSystem.IsMacOS())
{
Assert.True(systemChrome.TitleBarHeight > 0);
}
}
private void SetParameters(
bool extendClientArea,
bool forceSystemChrome,
bool preferSystemChrome,
bool macOsThickSystemChrome,
int titleBarHeight)
{
var extendClientAreaCheckBox = _session.FindElementByAccessibilityId("WindowExtendClientAreaToDecorationsHint");
var forceSystemChromeCheckBox = _session.FindElementByAccessibilityId("WindowForceSystemChrome");
var preferSystemChromeCheckBox = _session.FindElementByAccessibilityId("WindowPreferSystemChrome");
var macOsThickSystemChromeCheckBox = _session.FindElementByAccessibilityId("WindowMacThickSystemChrome");
var titleBarHeightBox = _session.FindElementByAccessibilityId("WindowTitleBarHeightHint");
if (extendClientAreaCheckBox.GetIsChecked() != extendClientArea)
extendClientAreaCheckBox.Click();
if (forceSystemChromeCheckBox.GetIsChecked() != forceSystemChrome)
forceSystemChromeCheckBox.Click();
if (preferSystemChromeCheckBox.GetIsChecked() != preferSystemChrome)
preferSystemChromeCheckBox.Click();
if (macOsThickSystemChromeCheckBox.GetIsChecked() != macOsThickSystemChrome)
macOsThickSystemChromeCheckBox.Click();
titleBarHeightBox.Click();
titleBarHeightBox.Clear();
if (titleBarHeight >= 0)
titleBarHeightBox.SendKeys(titleBarHeight.ToString());
}
private void ApplyToCurrentWindow()
{
var applyWindowDecorations = _session.FindElementByAccessibilityId("ApplyWindowDecorations");
applyWindowDecorations.Click();
}
private IDisposable ApplyOnNewWindow()
{
var showNewWindowDecorations = _session.FindElementByAccessibilityId("ShowNewWindowDecorations");
return showNewWindowDecorations.OpenWindowWithClick();
}
public void Dispose()
{
SetParameters(false, false, false, false, -1);
ApplyToCurrentWindow();
}
}

19
tests/Avalonia.IntegrationTests.Appium/WindowTests.cs

@ -263,8 +263,8 @@ namespace Avalonia.IntegrationTests.Appium
var showTopmostWindow = _session.FindElementByAccessibilityId("ShowTopmostWindow");
using var window = showTopmostWindow.OpenWindowWithClick();
Thread.Sleep(1000);
var ownerWindow = GetWindow("OwnerWindow");
var ownedWindow = GetWindow("OwnedWindow");
var ownerWindow = _session.GetWindowById("OwnerWindow");
var ownedWindow = _session.GetWindowById("OwnedWindow");
Assert.NotNull(ownerWindow);
Assert.NotNull(ownedWindow);
@ -297,7 +297,7 @@ namespace Avalonia.IntegrationTests.Appium
{
using (OpenWindow(null, mode, WindowStartupLocation.Manual, canResize: false, extendClientArea: extendClientArea))
{
var secondaryWindow = GetWindow("SecondaryWindow");
var secondaryWindow = _session.GetWindowById("SecondaryWindow");
AppiumWebElement? maximizeButton;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@ -442,19 +442,6 @@ namespace Avalonia.IntegrationTests.Appium
return showButton.OpenWindowWithClick();
}
private AppiumWebElement GetWindow(string identifier)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return _session.FindElementByXPath(
$"XCUIElementTypeWindow[@identifier='{identifier}']");
}
else
{
return _session.FindElementByXPath($"//Window[@AutomationId='{identifier}']");
}
}
private WindowInfo GetWindowInfo()
{
PixelRect? ReadOwnerRect()

8
tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs

@ -239,7 +239,7 @@ namespace Avalonia.IntegrationTests.Appium
public void Parent_Window_Has_Disabled_ChromeButtons_When_Modal_Dialog_Shown()
{
var window = GetWindow("MainWindow");
var windowChrome = window.GetChromeButtons();
var windowChrome = window.GetSystemChromeButtons();
Assert.True(windowChrome.Close!.Enabled);
Assert.True(windowChrome.FullScreen!.Enabled);
@ -260,7 +260,7 @@ namespace Avalonia.IntegrationTests.Appium
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.CenterOwner))
{
var secondaryWindow = GetWindow("SecondaryWindow");
var windowChrome = secondaryWindow.GetChromeButtons();
var windowChrome = secondaryWindow.GetSystemChromeButtons();
Assert.True(windowChrome.Close!.Enabled);
Assert.True(windowChrome.Maximize!.Enabled);
@ -374,7 +374,7 @@ namespace Avalonia.IntegrationTests.Appium
try
{
var chrome = secondaryWindow.GetChromeButtons();
var chrome = secondaryWindow.GetSystemChromeButtons();
if (decorations == SystemDecorations.Full)
{
@ -448,7 +448,7 @@ namespace Avalonia.IntegrationTests.Appium
private AppiumWebElement GetWindow(string identifier)
{
return _session.FindElementByXPath($"XCUIElementTypeWindow[@identifier='{identifier}']");
return _session.GetWindowById(identifier);
}
private int GetWindowOrder(string identifier)

Loading…
Cancel
Save