Browse Source

Merge branch 'master' into feature/UnicodeDataSpeedup

pull/8404/head
Benedikt Stebner 4 years ago
committed by GitHub
parent
commit
5acd47f2b9
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      samples/IntegrationTestApp/MainWindow.axaml
  2. 53
      samples/IntegrationTestApp/MainWindow.axaml.cs
  3. 25
      samples/IntegrationTestApp/ShowWindowTest.axaml
  4. 38
      samples/IntegrationTestApp/ShowWindowTest.axaml.cs
  5. 6
      src/Avalonia.Controls/TopLevel.cs
  6. 48
      src/Avalonia.Controls/Window.cs
  7. 10
      src/Avalonia.Controls/WindowBase.cs
  8. 27
      tests/Avalonia.Controls.UnitTests/WindowTests.cs
  9. 4
      tests/Avalonia.IntegrationTests.Appium/Avalonia.IntegrationTests.Appium.csproj
  10. 2
      tests/Avalonia.IntegrationTests.Appium/ButtonTests.cs
  11. 6
      tests/Avalonia.IntegrationTests.Appium/ComboBoxTests.cs
  12. 85
      tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs
  13. 2
      tests/Avalonia.IntegrationTests.Appium/ListBoxTests.cs
  14. 16
      tests/Avalonia.IntegrationTests.Appium/MenuTests.cs
  15. 2
      tests/Avalonia.IntegrationTests.Appium/NativeMenuTests.cs
  16. 32
      tests/Avalonia.IntegrationTests.Appium/PlatformFactAttribute.cs
  17. 125
      tests/Avalonia.IntegrationTests.Appium/WindowTests.cs
  18. 210
      tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs

20
samples/IntegrationTestApp/MainWindow.axaml

@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.MainWindow"
Name="MainWindow"
Title="IntegrationTestApp">
<NativeMenu.Menu>
<NativeMenu>
@ -94,6 +95,25 @@
</StackPanel>
</DockPanel>
</TabItem>
<TabItem Header="Window">
<StackPanel>
<TextBox Name="ShowWindowSize" Watermark="Window Size"/>
<ComboBox Name="ShowWindowMode" SelectedIndex="0">
<ComboBoxItem>NonOwned</ComboBoxItem>
<ComboBoxItem>Owned</ComboBoxItem>
<ComboBoxItem>Modal</ComboBoxItem>
</ComboBox>
<ComboBox Name="ShowWindowLocation" SelectedIndex="0">
<ComboBoxItem>Manual</ComboBoxItem>
<ComboBoxItem>CenterScreen</ComboBoxItem>
<ComboBoxItem>CenterOwner</ComboBoxItem>
</ComboBox>
<Button Name="ShowWindow">Show Window</Button>
<Button Name="SendToBack">Send to Back</Button>
<Button Name="ExitFullscreen">Exit Fullscreen</Button>
</StackPanel>
</TabItem>
</TabControl>
</DockPanel>
</Window>

53
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.VisualTree;
namespace IntegrationTestApp
{
@ -46,6 +48,51 @@ namespace IntegrationTestApp
}
}
private void ShowWindow()
{
var sizeTextBox = this.GetControl<TextBox>("ShowWindowSize");
var modeComboBox = this.GetControl<ComboBox>("ShowWindowMode");
var locationComboBox = this.GetControl<ComboBox>("ShowWindowLocation");
var size = !string.IsNullOrWhiteSpace(sizeTextBox.Text) ? Size.Parse(sizeTextBox.Text) : (Size?)null;
var owner = (Window)this.GetVisualRoot()!;
var window = new ShowWindowTest
{
WindowStartupLocation = (WindowStartupLocation)locationComboBox.SelectedIndex,
};
if (size.HasValue)
{
window.Width = size.Value.Width;
window.Height = size.Value.Height;
}
sizeTextBox.Text = string.Empty;
switch (modeComboBox.SelectedIndex)
{
case 0:
window.Show();
break;
case 1:
window.Show(owner);
break;
case 2:
window.ShowDialog(owner);
break;
}
}
private void SendToBack()
{
var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
foreach (var window in lifetime.Windows)
{
window.Activate();
}
}
private void MenuClicked(object? sender, RoutedEventArgs e)
{
var clickedMenuItemTextBlock = this.FindControl<TextBlock>("ClickedMenuItem");
@ -64,6 +111,12 @@ namespace IntegrationTestApp
this.FindControl<ListBox>("BasicListBox").SelectedIndex = -1;
if (source?.Name == "MenuClickedMenuItemReset")
this.FindControl<TextBlock>("ClickedMenuItem").Text = "None";
if (source?.Name == "ShowWindow")
ShowWindow();
if (source?.Name == "SendToBack")
SendToBack();
if (source?.Name == "ExitFullscreen")
WindowState = WindowState.Normal;
}
}
}

25
samples/IntegrationTestApp/ShowWindowTest.axaml

@ -0,0 +1,25 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="IntegrationTestApp.ShowWindowTest"
Name="SecondaryWindow"
Title="Show Window Test">
<Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<Label Grid.Column="0" Grid.Row="1">Client Size</Label>
<TextBox Name="ClientSize" Grid.Column="1" Grid.Row="1" IsReadOnly="True"/>
<Label Grid.Column="0" Grid.Row="2">Frame Size</Label>
<TextBox Name="FrameSize" Grid.Column="1" Grid.Row="2" IsReadOnly="True"/>
<Label Grid.Column="0" Grid.Row="3">Position</Label>
<TextBox Name="Position" Grid.Column="1" Grid.Row="3" IsReadOnly="True"/>
<Label Grid.Column="0" Grid.Row="4">Owner Rect</Label>
<TextBox Name="OwnerRect" Grid.Column="1" Grid.Row="4" IsReadOnly="True"/>
<Label Grid.Column="0" Grid.Row="5">Screen Rect</Label>
<TextBox Name="ScreenRect" Grid.Column="1" Grid.Row="5" IsReadOnly="True"/>
<Label Grid.Column="0" Grid.Row="6">Scaling</Label>
<TextBox Name="Scaling" Grid.Column="1" Grid.Row="6" IsReadOnly="True"/>
</Grid>
</Window>

38
samples/IntegrationTestApp/ShowWindowTest.axaml.cs

@ -0,0 +1,38 @@
using System;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Rendering;
namespace IntegrationTestApp
{
public class ShowWindowTest : Window
{
public ShowWindowTest()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
this.GetControl<TextBox>("ClientSize").Text = $"{Width}, {Height}";
this.GetControl<TextBox>("FrameSize").Text = $"{FrameSize}";
this.GetControl<TextBox>("Position").Text = $"{Position}";
this.GetControl<TextBox>("ScreenRect").Text = $"{Screens.ScreenFromVisual(this)?.WorkingArea}";
this.GetControl<TextBox>("Scaling").Text = $"{PlatformImpl?.DesktopScaling}";
if (Owner is not null)
{
var ownerRect = this.GetControl<TextBox>("OwnerRect");
var owner = (Window)Owner;
ownerRect.Text = $"{owner.Position}, {owner.FrameSize}";
}
}
}
}

6
src/Avalonia.Controls/TopLevel.cs

@ -484,7 +484,11 @@ namespace Avalonia.Controls
/// Raises the <see cref="Opened"/> event.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void OnOpened(EventArgs e) => Opened?.Invoke(this, e);
protected virtual void OnOpened(EventArgs e)
{
FrameSize = PlatformImpl?.FrameSize;
Opened?.Invoke(this, e);
}
/// <summary>
/// Raises the <see cref="Closed"/> event.

48
src/Avalonia.Controls/Window.cs

@ -871,10 +871,10 @@ namespace Avalonia.Controls
var scaling = owner?.DesktopScaling ?? PlatformImpl?.DesktopScaling ?? 1;
// TODO: We really need non-client size here.
var rect = new PixelRect(
PixelPoint.Origin,
PixelSize.FromSize(ClientSize, scaling));
// Use frame size, falling back to client size if the platform can't give it to us.
var rect = FrameSize.HasValue ?
new PixelRect(PixelSize.FromSize(FrameSize.Value, scaling)) :
new PixelRect(PixelSize.FromSize(ClientSize, scaling));
if (startupLocation == WindowStartupLocation.CenterScreen)
{
@ -991,28 +991,28 @@ namespace Avalonia.Controls
/// <inheritdoc/>
protected sealed override void HandleResized(Size clientSize, PlatformResizeReason reason)
{
if (ClientSize == clientSize)
return;
var sizeToContent = SizeToContent;
// If auto-sizing is enabled, and the resize came from a user resize (or the reason was
// unspecified) then turn off auto-resizing for any window dimension that is not equal
// to the requested size.
if (sizeToContent != SizeToContent.Manual &&
CanResize &&
reason == PlatformResizeReason.Unspecified ||
reason == PlatformResizeReason.User)
if (ClientSize != clientSize || double.IsNaN(Width) || double.IsNaN(Height))
{
if (clientSize.Width != ClientSize.Width)
sizeToContent &= ~SizeToContent.Width;
if (clientSize.Height != ClientSize.Height)
sizeToContent &= ~SizeToContent.Height;
SizeToContent = sizeToContent;
}
var sizeToContent = SizeToContent;
// If auto-sizing is enabled, and the resize came from a user resize (or the reason was
// unspecified) then turn off auto-resizing for any window dimension that is not equal
// to the requested size.
if (sizeToContent != SizeToContent.Manual &&
CanResize &&
reason == PlatformResizeReason.Unspecified ||
reason == PlatformResizeReason.User)
{
if (clientSize.Width != ClientSize.Width)
sizeToContent &= ~SizeToContent.Width;
if (clientSize.Height != ClientSize.Height)
sizeToContent &= ~SizeToContent.Height;
SizeToContent = sizeToContent;
}
Width = clientSize.Width;
Height = clientSize.Height;
Width = clientSize.Width;
Height = clientSize.Height;
}
base.HandleResized(clientSize, reason);
}

10
src/Avalonia.Controls/WindowBase.cs

@ -236,10 +236,14 @@ namespace Avalonia.Controls
/// <param name="reason">The reason for the resize.</param>
protected override void HandleResized(Size clientSize, PlatformResizeReason reason)
{
ClientSize = clientSize;
FrameSize = PlatformImpl?.FrameSize;
LayoutManager.ExecuteLayoutPass();
Renderer?.Resized(clientSize);
if (ClientSize != clientSize)
{
ClientSize = clientSize;
LayoutManager.ExecuteLayoutPass();
Renderer?.Resized(clientSize);
}
}
/// <summary>

27
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@ -695,6 +695,31 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Width_Height_Should_Not_Be_NaN_After_Show_With_SizeToContent_Manual()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var child = new Canvas
{
Width = 400,
Height = 800,
};
var target = new Window()
{
SizeToContent = SizeToContent.Manual,
Content = child
};
Show(target);
// Values come from MockWindowingPlatform defaults.
Assert.Equal(800, target.Width);
Assert.Equal(600, target.Height);
}
}
[Fact]
public void Width_Height_Should_Not_Be_NaN_After_Show_With_SizeToContent_WidthAndHeight()
{
@ -712,6 +737,8 @@ namespace Avalonia.Controls.UnitTests
Content = child
};
target.GetObservable(Window.WidthProperty).Subscribe(x => { });
Show(target);
Assert.Equal(400, target.Width);

4
tests/Avalonia.IntegrationTests.Appium/Avalonia.IntegrationTests.Appium.csproj

@ -5,6 +5,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Appium.WebDriver" Version="4.3.1" />
<PackageReference Include="Xunit.Extensions.Ordering" Version="1.4.5" />

2
tests/Avalonia.IntegrationTests.Appium/ButtonTests.cs

@ -44,7 +44,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("Button with TextBlock", button.Text);
}
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void ButtonWithAcceleratorKey()
{
var button = _session.FindElementByAccessibilityId("ButtonWithAcceleratorKey");

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

@ -46,7 +46,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("Item 0", comboBox.GetComboBoxValue());
}
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void Can_Change_Selection_With_Keyboard()
{
var comboBox = _session.FindElementByAccessibilityId("BasicComboBox");
@ -63,7 +63,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("Item 1", comboBox.GetComboBoxValue());
}
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void Can_Change_Selection_With_Keyboard_From_Unselected()
{
var comboBox = _session.FindElementByAccessibilityId("BasicComboBox");
@ -80,7 +80,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("Item 0", comboBox.GetComboBoxValue());
}
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void Can_Cancel_Keyboard_Selection_With_Escape()
{
var comboBox = _session.FindElementByAccessibilityId("BasicComboBox");

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

@ -1,8 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Runtime.InteropServices;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Interactions;
using Xunit;
namespace Avalonia.IntegrationTests.Appium
{
@ -11,6 +15,19 @@ namespace Avalonia.IntegrationTests.Appium
public static IReadOnlyList<AppiumWebElement> GetChildren(this AppiumWebElement element) =>
element.FindElementsByXPath("*/*");
public static (AppiumWebElement close, AppiumWebElement minimize, AppiumWebElement maximize) GetChromeButtons(this AppiumWebElement window)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
var closeButton = window.FindElementByXPath("//XCUIElementTypeButton[1]");
var fullscreenButton = window.FindElementByXPath("//XCUIElementTypeButton[2]");
var minimizeButton = window.FindElementByXPath("//XCUIElementTypeButton[3]");
return (closeButton, minimizeButton, fullscreenButton);
}
throw new NotSupportedException("GetChromeButtons not supported on this platform.");
}
public static string GetComboBoxValue(this AppiumWebElement element)
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
@ -43,6 +60,74 @@ namespace Avalonia.IntegrationTests.Appium
}
}
/// <summary>
/// Clicks a button which is expected to open a new window.
/// </summary>
/// <param name="element">The button to click.</param>
/// <returns>
/// An object which when disposed will cause the newly opened window to close.
/// </returns>
public static IDisposable OpenWindowWithClick(this AppiumWebElement element)
{
var session = element.WrappedDriver;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var oldHandle = session.CurrentWindowHandle;
var oldHandles = session.WindowHandles.ToList();
var oldChildWindows = session.FindElements(By.XPath("//Window"));
element.Click();
var newHandle = session.WindowHandles.Except(oldHandles).SingleOrDefault();
if (newHandle is not null)
{
// A new top-level window was opened. We need to switch to it.
session.SwitchTo().Window(newHandle);
return Disposable.Create(() =>
{
session.Close();
session.SwitchTo().Window(oldHandle);
});
}
else
{
// If a new window handle hasn't been added to the session then it's likely
// that a child window was opened. These don't appear in session.WindowHandles
// so we have to use an XPath query to get hold of it.
var newChildWindows = session.FindElements(By.XPath("//Window"));
var childWindow = Assert.Single(newChildWindows.Except(oldChildWindows));
return Disposable.Create(() =>
{
childWindow.SendKeys(Keys.Alt + Keys.F4 + Keys.Alt);
});
}
}
else
{
var oldWindows = session.FindElements(By.XPath("/XCUIElementTypeApplication/XCUIElementTypeWindow"));
var oldWindowTitles = oldWindows.ToDictionary(x => x.Text);
element.Click();
var newWindows = session.FindElements(By.XPath("/XCUIElementTypeApplication/XCUIElementTypeWindow"));
var newWindowTitles = newWindows.ToDictionary(x => x.Text);
var newWindowTitle = Assert.Single(newWindowTitles.Keys.Except(oldWindowTitles.Keys));
var newWindow = (AppiumWebElement)newWindowTitles[newWindowTitle];
return Disposable.Create(() =>
{
// TODO: We should be able to use Cmd+W here but Avalonia apps don't seem to have this shortcut
// set up by default.
var (close, _, _) = newWindow.GetChromeButtons();
close!.Click();
});
}
}
public static void SendClick(this AppiumWebElement element)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))

2
tests/Avalonia.IntegrationTests.Appium/ListBoxTests.cs

@ -61,7 +61,7 @@ namespace Avalonia.IntegrationTests.Appium
}
// appium-mac2-driver just hangs
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void Can_Select_Range_By_Shift_Clicking()
{
var listBox = GetTarget();

16
tests/Avalonia.IntegrationTests.Appium/MenuTests.cs

@ -57,7 +57,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("_Grandchild", clickedMenuItem.Text);
}
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void Select_Child_With_Alt_Arrow_Keys()
{
new Actions(_session)
@ -69,7 +69,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("_Child 1", clickedMenuItem.Text);
}
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void Select_Grandchild_With_Alt_Arrow_Keys()
{
new Actions(_session)
@ -81,7 +81,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("_Grandchild", clickedMenuItem.Text);
}
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void Select_Child_With_Alt_Access_Keys()
{
new Actions(_session)
@ -93,7 +93,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("_Child 1", clickedMenuItem.Text);
}
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void Select_Grandchild_With_Alt_Access_Keys()
{
new Actions(_session)
@ -105,7 +105,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("_Grandchild", clickedMenuItem.Text);
}
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void Select_Child_With_Click_Arrow_Keys()
{
var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem");
@ -119,7 +119,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("_Child 1", clickedMenuItem.Text);
}
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void Select_Grandchild_With_Click_Arrow_Keys()
{
var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem");
@ -133,7 +133,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("_Grandchild", clickedMenuItem.Text);
}
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void Child_AcceleratorKey()
{
var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem");
@ -145,7 +145,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal("Ctrl+O", childMenuItem.GetAttribute("AcceleratorKey"));
}
[PlatformFact(SkipOnOSX = true)]
[PlatformFact(TestPlatforms.Windows)]
public void PointerOver_Does_Not_Steal_Focus()
{
// Issue #7906

2
tests/Avalonia.IntegrationTests.Appium/NativeMenuTests.cs

@ -17,7 +17,7 @@ namespace Avalonia.IntegrationTests.Appium
tab.Click();
}
[PlatformFact(SkipOnWindows = true)]
[PlatformFact(TestPlatforms.MacOS)]
public void View_Menu_Select_Button_Tab()
{
var tabs = _session.FindElementByAccessibilityId("MainTabs");

32
tests/Avalonia.IntegrationTests.Appium/PlatformFactAttribute.cs

@ -5,21 +5,33 @@ using Xunit;
namespace Avalonia.IntegrationTests.Appium
{
[Flags]
internal enum TestPlatforms
{
Windows = 0x01,
MacOS = 0x02,
All = Windows | MacOS,
}
internal class PlatformFactAttribute : FactAttribute
{
public PlatformFactAttribute(TestPlatforms platforms = TestPlatforms.All) => Platforms = platforms;
public TestPlatforms Platforms { get; }
public override string? Skip
{
get
{
if (SkipOnWindows && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return "Ignored on Windows";
if (SkipOnOSX && RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return "Ignored on MacOS";
return null;
}
get => IsSupported() ? null : $"Ignored on {RuntimeInformation.OSDescription}";
set => throw new NotSupportedException();
}
public bool SkipOnOSX { get; set; }
public bool SkipOnWindows { get; set; }
private bool IsSupported()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return Platforms.HasAnyFlag(TestPlatforms.Windows);
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return Platforms.HasAnyFlag(TestPlatforms.MacOS);
return false;
}
}
}

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

@ -0,0 +1,125 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using OpenQA.Selenium.Appium;
using Xunit;
using Xunit.Sdk;
namespace Avalonia.IntegrationTests.Appium
{
[Collection("Default")]
public class WindowTests
{
private readonly AppiumDriver<AppiumWebElement> _session;
public WindowTests(TestAppFixture fixture)
{
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("Window");
tab.Click();
}
[Theory]
[MemberData(nameof(StartupLocationData))]
public void StartupLocation(PixelSize? size, ShowWindowMode mode, WindowStartupLocation location)
{
using var window = OpenWindow(size, mode, location);
var clientSize = Size.Parse(_session.FindElementByAccessibilityId("ClientSize").Text);
var frameSize = Size.Parse(_session.FindElementByAccessibilityId("FrameSize").Text);
var position = PixelPoint.Parse(_session.FindElementByAccessibilityId("Position").Text);
var screenRect = PixelRect.Parse(_session.FindElementByAccessibilityId("ScreenRect").Text);
var scaling = double.Parse(_session.FindElementByAccessibilityId("Scaling").Text);
Assert.True(frameSize.Width >= clientSize.Width, "Expected frame width >= client width.");
Assert.True(frameSize.Height > clientSize.Height, "Expected frame height > client height.");
var frameRect = new PixelRect(position, PixelSize.FromSize(frameSize, scaling));
switch (location)
{
case WindowStartupLocation.CenterScreen:
{
var expected = screenRect.CenterRect(frameRect);
AssertCloseEnough(expected.Position, frameRect.Position);
break;
}
}
}
public static TheoryData<PixelSize?, ShowWindowMode, WindowStartupLocation> StartupLocationData()
{
var sizes = new PixelSize?[] { null, new PixelSize(400, 300) };
var data = new TheoryData<PixelSize?, ShowWindowMode, WindowStartupLocation>();
foreach (var size in sizes)
{
foreach (var mode in Enum.GetValues<ShowWindowMode>())
{
foreach (var location in Enum.GetValues<WindowStartupLocation>())
{
if (!(location == WindowStartupLocation.CenterOwner && mode == ShowWindowMode.NonOwned))
{
data.Add(size, mode, location);
}
}
}
}
return data;
}
private static void AssertCloseEnough(PixelPoint expected, PixelPoint actual)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// On win32, accurate frame information cannot be obtained until a window is shown but
// WindowStartupLocation needs to be calculated before the window is shown, meaning that
// the position of a centered window can be off by a bit. From initial testing, looks
// like this shouldn't be more than 10 pixels.
if (Math.Abs(expected.X - actual.X) > 10)
throw new EqualException(expected, actual);
if (Math.Abs(expected.Y - actual.Y) > 10)
throw new EqualException(expected, actual);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
if (Math.Abs(expected.X - actual.X) > 15)
throw new EqualException(expected, actual);
if (Math.Abs(expected.Y - actual.Y) > 15)
throw new EqualException(expected, actual);
}
else
{
Assert.Equal(expected, actual);
}
}
private IDisposable OpenWindow(PixelSize? size, ShowWindowMode mode, WindowStartupLocation location)
{
var sizeTextBox = _session.FindElementByAccessibilityId("ShowWindowSize");
var modeComboBox = _session.FindElementByAccessibilityId("ShowWindowMode");
var locationComboBox = _session.FindElementByAccessibilityId("ShowWindowLocation");
var showButton = _session.FindElementByAccessibilityId("ShowWindow");
if (size.HasValue)
sizeTextBox.SendKeys($"{size.Value.Width}, {size.Value.Height}");
modeComboBox.Click();
_session.FindElementByName(mode.ToString()).SendClick();
locationComboBox.Click();
_session.FindElementByName(location.ToString()).SendClick();
return showButton.OpenWindowWithClick();
}
public enum ShowWindowMode
{
NonOwned,
Owned,
Modal
}
}
}

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

@ -0,0 +1,210 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Avalonia.Controls;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Interactions;
using Xunit;
namespace Avalonia.IntegrationTests.Appium
{
[Collection("Default")]
public class WindowTests_MacOS
{
private readonly AppiumDriver<AppiumWebElement> _session;
public WindowTests_MacOS(TestAppFixture fixture)
{
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("Window");
tab.Click();
}
[PlatformFact(TestPlatforms.MacOS)]
public void WindowOrder_Modal_Dialog_Stays_InFront_Of_Parent()
{
var mainWindow = _session.FindElementByAccessibilityId("MainWindow");
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.CenterOwner))
{
mainWindow.Click();
var windows = _session.FindElements(By.XPath("XCUIElementTypeWindow"));
var mainWindowIndex = GetWindowOrder(windows, "MainWindow");
var secondaryWindowIndex = GetWindowOrder(windows, "SecondaryWindow");
Assert.Equal(0, secondaryWindowIndex);
Assert.Equal(1, mainWindowIndex);
}
}
[PlatformFact(TestPlatforms.MacOS)]
public void WindowOrder_Modal_Dialog_Stays_InFront_Of_Parent_When_Clicking_Resize_Grip()
{
var mainWindow = FindWindow(_session, "MainWindow");
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.CenterOwner))
{
new Actions(_session)
.MoveToElement(mainWindow, 100, 1)
.ClickAndHold()
.Perform();
var windows = _session.FindElements(By.XPath("XCUIElementTypeWindow"));
var mainWindowIndex = GetWindowOrder(windows, "MainWindow");
var secondaryWindowIndex = GetWindowOrder(windows, "SecondaryWindow");
new Actions(_session)
.MoveToElement(mainWindow, 100, 1)
.Release()
.Perform();
Assert.Equal(0, secondaryWindowIndex);
Assert.Equal(1, mainWindowIndex);
}
}
[PlatformFact(TestPlatforms.MacOS)]
public void WindowOrder_Modal_Dialog_Stays_InFront_Of_Parent_When_In_Fullscreen()
{
var mainWindow = FindWindow(_session, "MainWindow");
var buttons = mainWindow.GetChromeButtons();
buttons.maximize.Click();
Thread.Sleep(500);
try
{
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.CenterOwner))
{
var windows = _session.FindElements(By.XPath("XCUIElementTypeWindow"));
var mainWindowIndex = GetWindowOrder(windows, "MainWindow");
var secondaryWindowIndex = GetWindowOrder(windows, "SecondaryWindow");
Assert.Equal(0, secondaryWindowIndex);
Assert.Equal(1, mainWindowIndex);
}
}
finally
{
_session.FindElementByAccessibilityId("ExitFullscreen").Click();
}
}
[PlatformFact(TestPlatforms.MacOS)]
public void WindowOrder_Owned_Dialog_Stays_InFront_Of_Parent()
{
var mainWindow = _session.FindElementByAccessibilityId("MainWindow");
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Owned, WindowStartupLocation.CenterOwner))
{
mainWindow.Click();
var windows = _session.FindElements(By.XPath("XCUIElementTypeWindow"));
var mainWindowIndex = GetWindowOrder(windows, "MainWindow");
var secondaryWindowIndex = GetWindowOrder(windows, "SecondaryWindow");
Assert.Equal(0, secondaryWindowIndex);
Assert.Equal(1, mainWindowIndex);
}
}
[PlatformFact(TestPlatforms.MacOS)]
public void WindowOrder_NonOwned_Window_Does_Not_Stay_InFront_Of_Parent()
{
var mainWindow = _session.FindElementByAccessibilityId("MainWindow");
using (OpenWindow(new PixelSize(1400, 100), ShowWindowMode.NonOwned, WindowStartupLocation.CenterOwner))
{
mainWindow.Click();
var windows = _session.FindElements(By.XPath("XCUIElementTypeWindow"));
var mainWindowIndex = GetWindowOrder(windows, "MainWindow");
var secondaryWindowIndex = GetWindowOrder(windows, "SecondaryWindow");
Assert.Equal(1, secondaryWindowIndex);
Assert.Equal(0, mainWindowIndex);
var sendToBack = _session.FindElementByAccessibilityId("SendToBack");
sendToBack.Click();
}
}
[PlatformFact(TestPlatforms.MacOS)]
public void Parent_Window_Has_Disabled_ChromeButtons_When_Modal_Dialog_Shown()
{
var window = FindWindow(_session, "MainWindow");
var (closeButton, miniaturizeButton, zoomButton) = window.GetChromeButtons();
Assert.True(closeButton.Enabled);
Assert.True(zoomButton.Enabled);
Assert.True(miniaturizeButton.Enabled);
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.CenterOwner))
{
Assert.False(closeButton.Enabled);
Assert.False(zoomButton.Enabled);
Assert.False(miniaturizeButton.Enabled);
}
}
[PlatformFact(TestPlatforms.MacOS)]
public void Minimize_Button_Is_Disabled_On_Modal_Dialog()
{
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.CenterOwner))
{
var secondaryWindow = FindWindow(_session, "SecondaryWindow");
var (closeButton, miniaturizeButton, zoomButton) = secondaryWindow.GetChromeButtons();
Assert.True(closeButton.Enabled);
Assert.True(zoomButton.Enabled);
Assert.False(miniaturizeButton.Enabled);
}
}
private IDisposable OpenWindow(PixelSize? size, ShowWindowMode mode, WindowStartupLocation location)
{
var sizeTextBox = _session.FindElementByAccessibilityId("ShowWindowSize");
var modeComboBox = _session.FindElementByAccessibilityId("ShowWindowMode");
var locationComboBox = _session.FindElementByAccessibilityId("ShowWindowLocation");
var showButton = _session.FindElementByAccessibilityId("ShowWindow");
if (size.HasValue)
sizeTextBox.SendKeys($"{size.Value.Width}, {size.Value.Height}");
modeComboBox.Click();
_session.FindElementByName(mode.ToString()).SendClick();
locationComboBox.Click();
_session.FindElementByName(location.ToString()).SendClick();
return showButton.OpenWindowWithClick();
}
private static int GetWindowOrder(IReadOnlyCollection<AppiumWebElement> elements, string identifier)
{
return elements.TakeWhile(x =>
x.FindElementByXPath("XCUIElementTypeWindow")?.GetAttribute("identifier") != identifier).Count();
}
private static AppiumWebElement FindWindow(AppiumDriver<AppiumWebElement> session, string identifier)
{
var windows = session.FindElementsByXPath("XCUIElementTypeWindow");
return windows.First(x =>
x.FindElementsByXPath("XCUIElementTypeWindow")
.Any(y => y.GetAttribute("identifier") == identifier));
}
public enum ShowWindowMode
{
NonOwned,
Owned,
Modal
}
}
}
Loading…
Cancel
Save