Browse Source

Refactor integration test app paging (#16551)

* Refactor IntegrationTestApp.

Use a `ListBox` to switch pages instead of a `TabControl`: the `TabControl` didn't adapt well to smaller screen sizes, and the `MainWindow` was getting unwieldy anyway.

* Update tests to use new pager.

Move logic for selecting the page to a base class as we may need to handle scrolling manually on macOS at some point (Appium on macOS doesn't scroll elements into view automatically).

* Add AutomationPeer.IsOffscreen.

This is needed in order for controls to be scrolled into view using WinAppDriver. The default is the same as WPF and the default value is overridden in the same controls as WPF (where present).
#Conflicts:
#	samples/IntegrationTestApp/App.axaml.cs
#	samples/IntegrationTestApp/MainWindow.axaml
#	samples/IntegrationTestApp/MainWindow.axaml.cs
#	samples/IntegrationTestApp/TopmostWindowTest.axaml.cs
#	src/Avalonia.Controls/TreeViewItem.cs
#	tests/Avalonia.IntegrationTests.Appium/ContextMenuTests.cs
#	tests/Avalonia.IntegrationTests.Appium/PointerTests.cs
#	tests/Avalonia.IntegrationTests.Appium/ScreenTests.cs
#	tests/Avalonia.IntegrationTests.Appium/TrayIconTests.cs
#	tests/Avalonia.IntegrationTests.Appium/WindowDecorationsTests.cs
#	tests/Avalonia.IntegrationTests.Appium/WindowTests.cs
#	tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
release/11.1.3
Steven Kirk 2 years ago
committed by Max Katz
parent
commit
0d4ebb941b
  1. 1
      samples/IntegrationTestApp/App.axaml.cs
  2. 3
      samples/IntegrationTestApp/IntegrationTestApp.csproj
  3. 200
      samples/IntegrationTestApp/MainWindow.axaml
  4. 292
      samples/IntegrationTestApp/MainWindow.axaml.cs
  5. 6
      samples/IntegrationTestApp/Models/Page.cs
  6. 17
      samples/IntegrationTestApp/Pages/AutomationPage.axaml
  7. 11
      samples/IntegrationTestApp/Pages/AutomationPage.axaml.cs
  8. 22
      samples/IntegrationTestApp/Pages/ButtonPage.axaml
  9. 11
      samples/IntegrationTestApp/Pages/ButtonPage.axaml.cs
  10. 12
      samples/IntegrationTestApp/Pages/CheckBoxPage.axaml
  11. 11
      samples/IntegrationTestApp/Pages/CheckBoxPage.axaml.cs
  12. 16
      samples/IntegrationTestApp/Pages/ComboBoxPage.axaml
  13. 22
      samples/IntegrationTestApp/Pages/ComboBoxPage.axaml.cs
  14. 17
      samples/IntegrationTestApp/Pages/ContextMenuPage.axaml
  15. 11
      samples/IntegrationTestApp/Pages/ContextMenuPage.axaml.cs
  16. 14
      samples/IntegrationTestApp/Pages/DesktopPage.axaml
  17. 19
      samples/IntegrationTestApp/Pages/DesktopPage.axaml.cs
  18. 29
      samples/IntegrationTestApp/Pages/GesturesPage.axaml
  19. 44
      samples/IntegrationTestApp/Pages/GesturesPage.axaml.cs
  20. 15
      samples/IntegrationTestApp/Pages/ListBoxPage.axaml
  21. 23
      samples/IntegrationTestApp/Pages/ListBoxPage.axaml.cs
  22. 22
      samples/IntegrationTestApp/Pages/MenuPage.axaml
  23. 24
      samples/IntegrationTestApp/Pages/MenuPage.axaml.cs
  24. 19
      samples/IntegrationTestApp/Pages/PointerPage.axaml
  25. 47
      samples/IntegrationTestApp/Pages/PointerPage.axaml.cs
  26. 14
      samples/IntegrationTestApp/Pages/RadioButtonPage.axaml
  27. 11
      samples/IntegrationTestApp/Pages/RadioButtonPage.axaml.cs
  28. 19
      samples/IntegrationTestApp/Pages/ScreensPage.axaml
  29. 32
      samples/IntegrationTestApp/Pages/ScreensPage.axaml.cs
  30. 8
      samples/IntegrationTestApp/Pages/ScrollBarPage.axaml
  31. 11
      samples/IntegrationTestApp/Pages/ScrollBarPage.axaml.cs
  32. 17
      samples/IntegrationTestApp/Pages/SliderPage.axaml
  33. 17
      samples/IntegrationTestApp/Pages/SliderPage.axaml.cs
  34. 21
      samples/IntegrationTestApp/Pages/WindowDecorationsPage.axaml
  35. 63
      samples/IntegrationTestApp/Pages/WindowDecorationsPage.axaml.cs
  36. 45
      samples/IntegrationTestApp/Pages/WindowPage.axaml
  37. 199
      samples/IntegrationTestApp/Pages/WindowPage.axaml.cs
  38. 1
      samples/IntegrationTestApp/ShowWindowTest.axaml.cs
  39. 23
      samples/IntegrationTestApp/ViewModels/MainWindowViewModel.cs
  40. 24
      samples/IntegrationTestApp/ViewModels/ViewModelBase.cs
  41. 2
      src/Avalonia.Controls.DataGrid/DataGridCell.cs
  42. 2
      src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
  43. 2
      src/Avalonia.Controls.DataGrid/DataGridRow.cs
  44. 6
      src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
  45. 12
      src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
  46. 14
      src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
  47. 2
      src/Avalonia.Controls/ListBoxItem.cs
  48. 2
      src/Avalonia.Controls/MenuItem.cs
  49. 1
      src/Avalonia.Controls/TabItem.cs
  50. 3
      src/Avalonia.Controls/TreeViewItem.cs
  51. 1
      src/Windows/Avalonia.Win32/Automation/AutomationNode.cs
  52. 21
      tests/Avalonia.IntegrationTests.Appium/AutomationTests.cs
  53. 24
      tests/Avalonia.IntegrationTests.Appium/ButtonTests.cs
  54. 19
      tests/Avalonia.IntegrationTests.Appium/CheckBoxTests.cs
  55. 53
      tests/Avalonia.IntegrationTests.Appium/ComboBoxTests.cs
  56. 16
      tests/Avalonia.IntegrationTests.Appium/ContextMenuTests.cs
  57. 81
      tests/Avalonia.IntegrationTests.Appium/GestureTests.cs
  58. 18
      tests/Avalonia.IntegrationTests.Appium/ListBoxTests.cs
  59. 73
      tests/Avalonia.IntegrationTests.Appium/MenuTests.cs
  60. 29
      tests/Avalonia.IntegrationTests.Appium/NativeMenuTests.cs
  61. 16
      tests/Avalonia.IntegrationTests.Appium/RadioButtonTests.cs
  62. 12
      tests/Avalonia.IntegrationTests.Appium/ScrollBarTests.cs
  63. 37
      tests/Avalonia.IntegrationTests.Appium/SliderTests.cs
  64. 33
      tests/Avalonia.IntegrationTests.Appium/TestBase.cs
  65. 84
      tests/Avalonia.IntegrationTests.Appium/WindowTests.cs
  66. 98
      tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs

1
samples/IntegrationTestApp/App.axaml.cs

@ -1,6 +1,7 @@
using Avalonia; using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using MiniMvvm;
namespace IntegrationTestApp namespace IntegrationTestApp
{ {

3
samples/IntegrationTestApp/IntegrationTestApp.csproj

@ -27,6 +27,9 @@
<ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
</ItemGroup> </ItemGroup>
<ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
</ItemGroup>
<Import Project="..\..\build\BuildTargets.targets" /> <Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\build\SampleApp.props" /> <Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\ReferenceCoreLibraries.props" /> <Import Project="..\..\build\ReferenceCoreLibraries.props" />

200
samples/IntegrationTestApp/MainWindow.axaml

@ -2,13 +2,13 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:integrationTestApp="using:IntegrationTestApp" xmlns:vm="using:IntegrationTestApp.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.MainWindow" x:Class="IntegrationTestApp.MainWindow"
Name="MainWindow" Name="MainWindow"
Icon="/Assets/icon.ico" Icon="/Assets/icon.ico"
Title="IntegrationTestApp" Title="IntegrationTestApp"
x:DataType="integrationTestApp:MainWindow"> x:DataType="vm:MainWindowViewModel">
<NativeMenu.Menu> <NativeMenu.Menu>
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="File"> <NativeMenuItem Header="File">
@ -19,7 +19,7 @@
<NativeMenuItem Header="View"> <NativeMenuItem Header="View">
<NativeMenu/> <NativeMenu/>
</NativeMenuItem> </NativeMenuItem>
<NativeMenuItem Header="_Options"> <NativeMenuItem Header="_Options">
<NativeMenu/> <NativeMenu/>
</NativeMenuItem> </NativeMenuItem>
</NativeMenu> </NativeMenu>
@ -28,185 +28,25 @@
<NativeMenuBar DockPanel.Dock="Top"/> <NativeMenuBar DockPanel.Dock="Top"/>
<StackPanel DockPanel.Dock="Bottom" Margin="4" Orientation="Horizontal"> <StackPanel DockPanel.Dock="Bottom" Margin="4" Orientation="Horizontal">
<TextBlock Margin="0,0,4,0">WindowState:</TextBlock> <TextBlock Margin="0,0,4,0">WindowState:</TextBlock>
<TextBlock Name="MainWindowState" Text="{Binding WindowState}"/> <TextBlock Name="MainWindowState" Text="{Binding $parent[Window].WindowState}"/>
<TextBlock Name="AppOverlayPopups" Margin="8 0"/> <TextBlock Name="AppOverlayPopups" Margin="8 0"/>
</StackPanel> </StackPanel>
<TabControl TabStripPlacement="Left" Name="MainTabs">
<TabItem Header="Automation">
<StackPanel>
<TextBlock Name="TextBlockWithName">TextBlockWithName</TextBlock>
<TextBlock Name="NotTheAutomationId" AutomationProperties.AutomationId="TextBlockWithNameAndAutomationId">
TextBlockWithNameAndAutomationId
</TextBlock>
<TextBlock Name="TextBlockAsLabel">Label for TextBox</TextBlock>
<TextBox Name="LabeledByTextBox" AutomationProperties.LabeledBy="{Binding #TextBlockAsLabel}">
Foo
</TextBox>
</StackPanel>
</TabItem>
<TabItem Header="Button">
<StackPanel>
<Button Name="DisabledButton" IsEnabled="False">
Disabled Button
</Button>
<Button Name="EffectivelyDisabledButton" Command="{ReflectionBinding DoesntExist}">
Effectively Disabled Button
</Button>
<Button Name="BasicButton">
Basic Button
</Button>
<Button Name="ButtonWithTextBlock">
<TextBlock>Button with TextBlock</TextBlock>
</Button>
<Button Name="ButtonWithAcceleratorKey" HotKey="Ctrl+B">Button with Accelerator Key</Button>
</StackPanel>
</TabItem>
<TabItem Header="RadioButton">
<StackPanel Orientation="Vertical">
<RadioButton Name="BasicRadioButton">Sample RadioButton</RadioButton>
<StackPanel Orientation="Vertical">
<RadioButton Name="ThreeStatesRadioButton1" IsChecked="True" IsThreeState="True">Three States: Option 1</RadioButton>
<RadioButton Name="ThreeStatesRadioButton2" IsChecked="False" IsThreeState="True">Three States: Option 2</RadioButton>
</StackPanel>
</StackPanel>
</TabItem>
<TabItem Header="CheckBox">
<StackPanel>
<CheckBox Name="UncheckedCheckBox">Unchecked</CheckBox>
<CheckBox Name="CheckedCheckBox" IsChecked="True">Checked</CheckBox>
<CheckBox Name="ThreeStateCheckBox" IsThreeState="True" IsChecked="{x:Null}">ThreeState</CheckBox>
</StackPanel>
</TabItem>
<TabItem Header="ComboBox">
<StackPanel>
<ComboBox Name="BasicComboBox">
<ComboBoxItem>Item 0</ComboBoxItem>
<ComboBoxItem>Item 1</ComboBoxItem>
</ComboBox>
<CheckBox Name="ComboBoxWrapSelection" IsChecked="{Binding #BasicComboBox.WrapSelection}">Wrap Selection</CheckBox>
<Button Name="ComboBoxSelectionClear">Clear Selection</Button>
<Button Name="ComboBoxSelectFirst">Select First</Button>
</StackPanel>
</TabItem>
<TabItem Header="ContextMenu"> <DockPanel>
<StackPanel> <ListBox Name="Pager"
<Button Name="ShowContextMenu" Content="Right-click to show context menu."> DockPanel.Dock="Left"
<Button.ContextMenu> DisplayMemberBinding="{Binding Name}"
<ContextMenu> ItemsSource="{Binding Pages}"
<MenuItem Name="ContextMenuItem1" Header="Item 1"/> SelectedItem="{Binding SelectedPage}"
<MenuItem Name="ContextMenuItem2" Header="Item 2"/> SelectionChanged="Pager_SelectionChanged">
</ContextMenu> <ListBox.ItemsPanel>
</Button.ContextMenu> <ItemsPanelTemplate>
</Button> <StackPanel/>
</StackPanel> </ItemsPanelTemplate>
</TabItem> </ListBox.ItemsPanel>
</ListBox>
<TabItem Header="Gestures"> <Decorator Name="PagerContent"/>
<DockPanel> </DockPanel>
<DockPanel DockPanel.Dock="Top">
<Button Name="ResetGestures" DockPanel.Dock="Right">Reset</Button>
<TextBlock Name="LastGesture" />
</DockPanel>
<Panel>
<Border Name="GestureBorder" Background="Blue"
AutomationProperties.AccessibilityView="Content"
AutomationProperties.ControlTypeOverride="Image"/>
<Border Name="GestureBorder2" Background="Green" IsVisible="False"
AutomationProperties.AccessibilityView="Content"
AutomationProperties.ControlTypeOverride="Image"/>
</Panel>
</DockPanel>
</TabItem>
<TabItem Header="ListBox">
<DockPanel>
<StackPanel DockPanel.Dock="Bottom">
<Button Name="ListBoxSelectionClear">Clear Selection</Button>
</StackPanel>
<ListBox Name="BasicListBox" ItemsSource="{Binding ListBoxItems}" SelectionMode="Multiple"/>
</DockPanel>
</TabItem>
<TabItem Header="Menu">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Name="RootMenuItem" Header="_Root">
<MenuItem Name="Child1MenuItem" Header="_Child 1" InputGesture="Ctrl+O" Click="MenuClicked"/>
<MenuItem Name="Child2MenuItem" Header="C_hild 2">
<MenuItem Name="GrandchildMenuItem" Header="_Grandchild" Click="MenuClicked"/>
</MenuItem>
</MenuItem>
</Menu>
<StackPanel>
<TextBlock Name="ClickedMenuItem">None</TextBlock>
<Button Name="MenuClickedMenuItemReset">Reset</Button>
<TextBox Name="MenuFocusTest"/>
</StackPanel>
</DockPanel>
</TabItem>
<TabItem Header="Window">
<Grid ColumnDefinitions="*,8,*">
<StackPanel Grid.Column="0">
<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>
<ComboBox Name="ShowWindowState" SelectedIndex="0">
<ComboBoxItem Name="ShowWindowStateNormal">Normal</ComboBoxItem>
<ComboBoxItem Name="ShowWindowStateMinimized">Minimized</ComboBoxItem>
<ComboBoxItem Name="ShowWindowStateMaximized">Maximized</ComboBoxItem>
<ComboBoxItem Name="ShowWindowStateFullScreen">FullScreen</ComboBoxItem>
</ComboBox>
<ComboBox Name="ShowWindowSystemDecorations" SelectedIndex="2">
<ComboBoxItem Name="ShowWindowSystemDecorationsNone">None</ComboBoxItem>
<ComboBoxItem Name="ShowWindowSystemDecorationsBorderOnly">BorderOnly</ComboBoxItem>
<ComboBoxItem Name="ShowWindowSystemDecorationsFull">Full</ComboBoxItem>
</ComboBox>
<CheckBox Name="ShowWindowExtendClientAreaToDecorationsHint">ExtendClientAreaToDecorationsHint</CheckBox>
<CheckBox Name="ShowWindowCanResize" IsChecked="True">Can Resize</CheckBox>
<Button Name="ShowWindow">Show Window</Button>
<Button Name="SendToBack">Send to Back</Button>
<Button Name="EnterFullscreen">Enter Fullscreen</Button>
<Button Name="ExitFullscreen">Exit Fullscreen</Button>
<Button Name="RestoreAll">Restore All</Button>
</StackPanel>
<StackPanel Grid.Column="2">
<Button Name="ShowTransparentWindow">Transparent Window</Button>
<Button Name="ShowTransparentPopup">Transparent Popup</Button>
</StackPanel>
</Grid>
</TabItem>
<TabItem Header="Slider">
<DockPanel>
<DockPanel DockPanel.Dock="Top">
<TextBox Name="HorizontalSliderValue"
DockPanel.Dock="Right"
Text="{Binding #HorizontalSlider.Value, Mode=OneWay, StringFormat=\{0:0\}}"
VerticalAlignment="Top"/>
<Slider Name="HorizontalSlider" Value="50"/>
</DockPanel>
<Button Name="ResetSliders">Reset</Button>
</DockPanel>
</TabItem>
<TabItem Header="ScrollBar">
<ScrollBar Name="MyScrollBar" Orientation="Horizontal" AllowAutoHide="False" Width="200" Height="30" Value="20"/>
</TabItem>
</TabControl>
</DockPanel> </DockPanel>
</Window> </Window>

292
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -1,17 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Automation;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using IntegrationTestApp.Models;
using Avalonia.Controls.Primitives; using IntegrationTestApp.Pages;
using Avalonia.Controls.Primitives.PopupPositioning; using IntegrationTestApp.ViewModels;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.VisualTree;
using Microsoft.CodeAnalysis;
namespace IntegrationTestApp namespace IntegrationTestApp
{ {
@ -20,270 +11,67 @@ namespace IntegrationTestApp
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
InitializeViewMenu();
InitializeGesturesTab();
this.AttachDevTools();
var overlayPopups = this.Get<TextBlock>("AppOverlayPopups"); var viewModel = new MainWindowViewModel(CreatePages());
overlayPopups.Text = Program.OverlayPopups ? "Overlay Popups" : "Native Popups"; InitializeViewMenu(viewModel.Pages);
AddHandler(Button.ClickEvent, OnButtonClick); DataContext = viewModel;
ListBoxItems = Enumerable.Range(0, 100).Select(x => "Item " + x).ToList(); AppOverlayPopups.Text = Program.OverlayPopups ? "Overlay Popups" : "Native Popups";
DataContext = this;
} }
public List<string> ListBoxItems { get; } private MainWindowViewModel? ViewModel => (MainWindowViewModel?)DataContext;
private void InitializeComponent() private void InitializeViewMenu(IEnumerable<Page> pages)
{
AvaloniaXamlLoader.Load(this);
}
private void InitializeViewMenu()
{ {
var mainTabs = this.Get<TabControl>("MainTabs"); var mainTabs = this.Get<TabControl>("MainTabs");
var viewMenu = (NativeMenuItem?)NativeMenu.GetMenu(this)?.Items[1]; var viewMenu = (NativeMenuItem?)NativeMenu.GetMenu(this)?.Items[1];
foreach (var tabItem in mainTabs.Items.Cast<TabItem>()) foreach (var page in pages)
{ {
var menuItem = new NativeMenuItem var menuItem = new NativeMenuItem
{ {
Header = (string?)tabItem.Header, Header = (string?)page.Name,
ToolTip = $"Tip:{(string?)tabItem.Header}", ToolTip = $"Tip:{(string?)page.Name}",
IsChecked = tabItem.IsSelected,
ToggleType = NativeMenuItemToggleType.Radio, ToggleType = NativeMenuItemToggleType.Radio,
}; };
menuItem.Click += (_, _) => tabItem.IsSelected = true; menuItem.Click += (_, _) =>
viewMenu?.Menu?.Items.Add(menuItem);
}
}
private void ShowWindow()
{
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 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 owner = (Window)this.GetVisualRoot()!;
var window = new ShowWindowTest
{
WindowStartupLocation = (WindowStartupLocation)locationComboBox.SelectedIndex,
CanResize = canResizeCheckBox.IsChecked ?? false,
};
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
{
// Make sure the windows have unique names and AutomationIds.
var existing = lifetime.Windows.OfType<ShowWindowTest>().Count();
if (existing > 0)
{ {
AutomationProperties.SetAutomationId(window, window.Name + (existing + 1)); if (ViewModel is { } viewModel)
window.Title += $" {existing + 1}"; viewModel.SelectedPage = page;
} };
}
if (size.HasValue)
{
window.Width = size.Value.Width;
window.Height = size.Value.Height;
}
sizeTextBox.Text = string.Empty;
window.ExtendClientAreaToDecorationsHint = extendClientArea.IsChecked ?? false;
window.SystemDecorations = (SystemDecorations)systemDecorations.SelectedIndex;
window.WindowState = (WindowState)stateComboBox.SelectedIndex;
switch (modeComboBox.SelectedIndex)
{
case 0:
window.Show();
break;
case 1:
window.Show(owner);
break;
case 2:
window.ShowDialog(owner);
break;
}
}
private void ShowTransparentWindow()
{
// Show a background window to make sure the color behind the transparent window is
// a known color (green).
var backgroundWindow = new Window
{
Title = "Transparent Window Background",
Name = "TransparentWindowBackground",
Width = 300,
Height = 300,
Background = Brushes.Green,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
// This is the transparent window with a red circle.
var window = new Window
{
Title = "Transparent Window",
Name = "TransparentWindow",
SystemDecorations = SystemDecorations.None,
Background = Brushes.Transparent,
TransparencyLevelHint = new[] { WindowTransparencyLevel.Transparent },
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Width = 200,
Height = 200,
Content = new Border
{
Background = Brushes.Red,
CornerRadius = new CornerRadius(100),
}
};
window.PointerPressed += (_, _) =>
{
window.Close();
backgroundWindow.Close();
};
backgroundWindow.Show(this);
window.Show(backgroundWindow);
}
private void ShowTransparentPopup()
{
var popup = new Popup
{
WindowManagerAddShadowHint = false,
Placement = PlacementMode.AnchorAndGravity,
PlacementAnchor = PopupAnchor.Top,
PlacementGravity = PopupGravity.Bottom,
Width= 200,
Height= 200,
Child = new Border
{
Background = Brushes.Red,
CornerRadius = new CornerRadius(100),
}
};
// Show a background window to make sure the color behind the transparent window is
// a known color (green).
var backgroundWindow = new Window
{
Title = "Transparent Popup Background",
Name = "TransparentPopupBackground",
Width = 200,
Height = 200,
Background = Brushes.Green,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Content = new Border
{
Name = "PopupContainer",
Child = popup,
[AutomationProperties.AccessibilityViewProperty] = AccessibilityView.Content,
}
};
backgroundWindow.PointerPressed += (_, _) => backgroundWindow.Close();
backgroundWindow.Show(this);
popup.Open();
}
private void SendToBack()
{
var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
foreach (var window in lifetime.Windows.ToArray())
{
window.Activate();
}
}
private void RestoreAll()
{
var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
foreach (var window in lifetime.Windows.ToArray()) viewMenu?.Menu?.Items.Add(menuItem);
{
window.Show();
if (window.WindowState == WindowState.Minimized)
window.WindowState = WindowState.Normal;
} }
} }
private void InitializeGesturesTab() private void Pager_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {
var gestureBorder = this.GetControl<Border>("GestureBorder"); if (Pager.SelectedItem is Page page)
var gestureBorder2 = this.GetControl<Border>("GestureBorder2"); PagerContent.Child = page.CreateContent();
var lastGesture = this.GetControl<TextBlock>("LastGesture");
var resetGestures = this.GetControl<Button>("ResetGestures");
gestureBorder.Tapped += (_, _) => lastGesture.Text = "Tapped";
gestureBorder.DoubleTapped += (_, _) =>
{
lastGesture.Text = "DoubleTapped";
// Testing #8733
gestureBorder.IsVisible = false;
gestureBorder2.IsVisible = true;
};
gestureBorder2.DoubleTapped += (_, _) =>
{
lastGesture.Text = "DoubleTapped2";
};
Gestures.AddRightTappedHandler(gestureBorder, (_, _) => lastGesture.Text = "RightTapped");
resetGestures.Click += (_, _) =>
{
lastGesture.Text = string.Empty;
gestureBorder.IsVisible = true;
gestureBorder2.IsVisible = false;
};
} }
private void MenuClicked(object? sender, RoutedEventArgs e) private static IEnumerable<Page> CreatePages()
{ {
var clickedMenuItemTextBlock = this.Get<TextBlock>("ClickedMenuItem"); return
clickedMenuItemTextBlock.Text = (sender as MenuItem)?.Header?.ToString(); [
} new("Automation", () => new AutomationPage()),
new("Button", () => new ButtonPage()),
private void OnButtonClick(object? sender, RoutedEventArgs e) new("CheckBox", () => new CheckBoxPage()),
{ new("ComboBox", () => new ComboBoxPage()),
var source = e.Source as Button; new("ContextMenu", () => new ContextMenuPage()),
new("DesktopPage", () => new DesktopPage()),
if (source?.Name == "ComboBoxSelectionClear") new("Gestures", () => new GesturesPage()),
this.Get<ComboBox>("BasicComboBox").SelectedIndex = -1; new("ListBox", () => new ListBoxPage()),
if (source?.Name == "ComboBoxSelectFirst") new("Menu", () => new MenuPage()),
this.Get<ComboBox>("BasicComboBox").SelectedIndex = 0; new("Pointer", () => new PointerPage()),
if (source?.Name == "ListBoxSelectionClear") new("RadioButton", () => new RadioButtonPage()),
this.Get<ListBox>("BasicListBox").SelectedIndex = -1; new("Screens", () => new ScreensPage()),
if (source?.Name == "MenuClickedMenuItemReset") new("ScrollBar", () => new ScrollBarPage()),
this.Get<TextBlock>("ClickedMenuItem").Text = "None"; new("Slider", () => new SliderPage()),
if (source?.Name == "ResetSliders") new("Window Decorations", () => new WindowDecorationsPage()),
this.Get<Slider>("HorizontalSlider").Value = 50; new("Window", () => new WindowPage()),
if (source?.Name == "ShowTransparentWindow") ];
ShowTransparentWindow();
if (source?.Name == "ShowTransparentPopup")
ShowTransparentPopup();
if (source?.Name == "ShowWindow")
ShowWindow();
if (source?.Name == "SendToBack")
SendToBack();
if (source?.Name == "EnterFullscreen")
WindowState = WindowState.FullScreen;
if (source?.Name == "ExitFullscreen")
WindowState = WindowState.Normal;
if (source?.Name == "RestoreAll")
RestoreAll();
} }
} }
} }

6
samples/IntegrationTestApp/Models/Page.cs

@ -0,0 +1,6 @@
using System;
using Avalonia.Controls;
namespace IntegrationTestApp.Models;
internal record Page(string Name, Func<Control> CreateContent);

17
samples/IntegrationTestApp/Pages/AutomationPage.axaml

@ -0,0 +1,17 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.AutomationPage">
<StackPanel>
<TextBlock Name="TextBlockWithName">TextBlockWithName</TextBlock>
<TextBlock Name="NotTheAutomationId" AutomationProperties.AutomationId="TextBlockWithNameAndAutomationId">
TextBlockWithNameAndAutomationId
</TextBlock>
<TextBlock Name="TextBlockAsLabel">Label for TextBox</TextBlock>
<TextBox Name="LabeledByTextBox" AutomationProperties.LabeledBy="{Binding #TextBlockAsLabel}">
Foo
</TextBox>
</StackPanel>
</UserControl>

11
samples/IntegrationTestApp/Pages/AutomationPage.axaml.cs

@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace IntegrationTestApp.Pages;
public partial class AutomationPage : UserControl
{
public AutomationPage()
{
InitializeComponent();
}
}

22
samples/IntegrationTestApp/Pages/ButtonPage.axaml

@ -0,0 +1,22 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.ButtonPage">
<StackPanel>
<Button Name="DisabledButton" IsEnabled="False">
Disabled Button
</Button>
<Button Name="EffectivelyDisabledButton" Command="{ReflectionBinding DoesntExist}">
Effectively Disabled Button
</Button>
<Button Name="BasicButton">
Basic Button
</Button>
<Button Name="ButtonWithTextBlock">
<TextBlock>Button with TextBlock</TextBlock>
</Button>
<Button Name="ButtonWithAcceleratorKey" HotKey="Ctrl+B">Button with Accelerator Key</Button>
</StackPanel>
</UserControl>

11
samples/IntegrationTestApp/Pages/ButtonPage.axaml.cs

@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace IntegrationTestApp.Pages;
public partial class ButtonPage : UserControl
{
public ButtonPage()
{
InitializeComponent();
}
}

12
samples/IntegrationTestApp/Pages/CheckBoxPage.axaml

@ -0,0 +1,12 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.CheckBoxPage">
<StackPanel>
<CheckBox Name="UncheckedCheckBox">Unchecked</CheckBox>
<CheckBox Name="CheckedCheckBox" IsChecked="True">Checked</CheckBox>
<CheckBox Name="ThreeStateCheckBox" IsThreeState="True" IsChecked="{x:Null}">ThreeState</CheckBox>
</StackPanel>
</UserControl>

11
samples/IntegrationTestApp/Pages/CheckBoxPage.axaml.cs

@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace IntegrationTestApp.Pages;
public partial class CheckBoxPage : UserControl
{
public CheckBoxPage()
{
InitializeComponent();
}
}

16
samples/IntegrationTestApp/Pages/ComboBoxPage.axaml

@ -0,0 +1,16 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.ComboBoxPage">
<StackPanel>
<ComboBox Name="BasicComboBox">
<ComboBoxItem>Item 0</ComboBoxItem>
<ComboBoxItem>Item 1</ComboBoxItem>
</ComboBox>
<CheckBox Name="ComboBoxWrapSelection" IsChecked="{Binding #BasicComboBox.WrapSelection}">Wrap Selection</CheckBox>
<Button Name="ComboBoxSelectionClear" Click="ComboBoxSelectionClear_Click">Clear Selection</Button>
<Button Name="ComboBoxSelectFirst" Click="ComboBoxSelectFirst_Click">Select First</Button>
</StackPanel>
</UserControl>

22
samples/IntegrationTestApp/Pages/ComboBoxPage.axaml.cs

@ -0,0 +1,22 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace IntegrationTestApp.Pages;
public partial class ComboBoxPage : UserControl
{
public ComboBoxPage()
{
InitializeComponent();
}
private void ComboBoxSelectionClear_Click(object? sender, RoutedEventArgs e)
{
BasicComboBox.SelectedIndex = -1;
}
private void ComboBoxSelectFirst_Click(object? sender, RoutedEventArgs e)
{
BasicComboBox.SelectedIndex = 0;
}
}

17
samples/IntegrationTestApp/Pages/ContextMenuPage.axaml

@ -0,0 +1,17 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.ContextMenuPage">
<StackPanel>
<Button Name="ShowContextMenu" Content="Right-click to show context menu.">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Name="ContextMenuItem1" Header="Item 1"/>
<MenuItem Name="ContextMenuItem2" Header="Item 2"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
</UserControl>

11
samples/IntegrationTestApp/Pages/ContextMenuPage.axaml.cs

@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace IntegrationTestApp.Pages;
public partial class ContextMenuPage : UserControl
{
public ContextMenuPage()
{
InitializeComponent();
}
}

14
samples/IntegrationTestApp/Pages/DesktopPage.axaml

@ -0,0 +1,14 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.DesktopPage">
<StackPanel>
<CheckBox x:FieldModifier="public" Name="TrayIconClicked">Tray Icon Clicked</CheckBox>
<CheckBox x:FieldModifier="public" Name="TrayIconMenuClicked">Tray Icon Menu Clicked</CheckBox>
<Button Name="ToggleTrayIconVisible"
Content="Toggle TrayIcon Visible"
Click="ToggleTrayIconVisible_Click"/>
</StackPanel>
</UserControl>

19
samples/IntegrationTestApp/Pages/DesktopPage.axaml.cs

@ -0,0 +1,19 @@
using System.Linq;
using Avalonia;
using Avalonia.Controls;
namespace IntegrationTestApp.Pages;
public partial class DesktopPage : UserControl
{
public DesktopPage()
{
InitializeComponent();
}
private void ToggleTrayIconVisible_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
var icon = TrayIcon.GetIcons(Application.Current!)!.FirstOrDefault()!;
icon.IsVisible = !icon.IsVisible;
}
}

29
samples/IntegrationTestApp/Pages/GesturesPage.axaml

@ -0,0 +1,29 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.GesturesPage">
<DockPanel>
<DockPanel DockPanel.Dock="Top">
<Button Name="ResetGestures"
DockPanel.Dock="Right"
Click="ResetGestures_Click">
Reset
</Button>
<TextBlock Name="LastGesture" />
</DockPanel>
<Panel>
<Border Name="GestureBorder" Background="Blue"
AutomationProperties.AccessibilityView="Content"
AutomationProperties.ControlTypeOverride="Image"
Tapped="GestureBorder_Tapped"
DoubleTapped="GestureBorder_DoubleTapped"
Gestures.RightTapped="GestureBorder_RightTapped"/>
<Border Name="GestureBorder2" Background="Green" IsVisible="False"
AutomationProperties.AccessibilityView="Content"
AutomationProperties.ControlTypeOverride="Image"
DoubleTapped="GestureBorder2_DoubleTapped"/>
</Panel>
</DockPanel>
</UserControl>

44
samples/IntegrationTestApp/Pages/GesturesPage.axaml.cs

@ -0,0 +1,44 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
namespace IntegrationTestApp.Pages;
public partial class GesturesPage : UserControl
{
public GesturesPage()
{
InitializeComponent();
}
private void GestureBorder_Tapped(object? sender, TappedEventArgs e)
{
LastGesture.Text = "Tapped";
}
private void GestureBorder_DoubleTapped(object? sender, TappedEventArgs e)
{
LastGesture.Text = "DoubleTapped";
// Testing #8733
GestureBorder.IsVisible = false;
GestureBorder2.IsVisible = true;
}
private void GestureBorder_RightTapped(object? sender, RoutedEventArgs e)
{
LastGesture.Text = "RightTapped";
}
private void GestureBorder2_DoubleTapped(object? sender, TappedEventArgs e)
{
LastGesture.Text = "DoubleTapped2";
}
private void ResetGestures_Click(object? sender, RoutedEventArgs e)
{
LastGesture.Text = string.Empty;
GestureBorder.IsVisible = true;
GestureBorder2.IsVisible = false;
}
}

15
samples/IntegrationTestApp/Pages/ListBoxPage.axaml

@ -0,0 +1,15 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pages="using:IntegrationTestApp.Pages"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.ListBoxPage"
x:DataType="pages:ListBoxPage">
<DockPanel>
<StackPanel DockPanel.Dock="Bottom">
<Button Name="ListBoxSelectionClear" Click="ListBoxSelectionClear_Click">Clear Selection</Button>
</StackPanel>
<ListBox Name="BasicListBox" ItemsSource="{Binding ListBoxItems}" SelectionMode="Multiple"/>
</DockPanel>
</UserControl>

23
samples/IntegrationTestApp/Pages/ListBoxPage.axaml.cs

@ -0,0 +1,23 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace IntegrationTestApp.Pages;
public partial class ListBoxPage : UserControl
{
public ListBoxPage()
{
InitializeComponent();
ListBoxItems = Enumerable.Range(0, 100).Select(x => "Item " + x).ToList();
DataContext = this;
}
public List<string> ListBoxItems { get; }
private void ListBoxSelectionClear_Click(object? sender, RoutedEventArgs e)
{
BasicListBox.SelectedIndex = -1;
}
}

22
samples/IntegrationTestApp/Pages/MenuPage.axaml

@ -0,0 +1,22 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.MenuPage">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Name="RootMenuItem" Header="_Root">
<MenuItem Name="Child1MenuItem" Header="_Child 1" InputGesture="Ctrl+O" Click="MenuClicked"/>
<MenuItem Name="Child2MenuItem" Header="C_hild 2">
<MenuItem Name="GrandchildMenuItem" Header="_Grandchild" Click="MenuClicked"/>
</MenuItem>
</MenuItem>
</Menu>
<StackPanel>
<TextBlock Name="ClickedMenuItem">None</TextBlock>
<Button Name="MenuClickedMenuItemReset" Click="MenuClickedMenuItemReset_Click">Reset</Button>
<TextBox Name="MenuFocusTest"/>
</StackPanel>
</DockPanel>
</UserControl>

24
samples/IntegrationTestApp/Pages/MenuPage.axaml.cs

@ -0,0 +1,24 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace IntegrationTestApp.Pages;
public partial class MenuPage : UserControl
{
public MenuPage()
{
InitializeComponent();
}
private void MenuClicked(object? sender, RoutedEventArgs e)
{
var clickedMenuItemTextBlock = ClickedMenuItem;
clickedMenuItemTextBlock.Text = (sender as MenuItem)?.Header?.ToString();
}
private void MenuClickedMenuItemReset_Click(object? sender, RoutedEventArgs e)
{
ClickedMenuItem.Text = "None";
}
}

19
samples/IntegrationTestApp/Pages/PointerPage.axaml

@ -0,0 +1,19 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.PointerPage">
<StackPanel>
<!-- Trigger with PointerPressed rather using a Button so we have access to the pointer. -->
<Border Name="PointerPageShowDialog"
Background="{DynamicResource ButtonBackground}"
HorizontalAlignment="Left"
Padding="{DynamicResource ButtonPadding}"
AutomationProperties.AccessibilityView="Control"
PointerPressed="PointerPageShowDialog_PointerPressed">
<TextBlock>Show Dialog</TextBlock>
</Border>
<TextBlock Name="PointerCaptureStatus"/>
</StackPanel>
</UserControl>

47
samples/IntegrationTestApp/Pages/PointerPage.axaml.cs

@ -0,0 +1,47 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
namespace IntegrationTestApp.Pages;
public partial class PointerPage : UserControl
{
public PointerPage()
{
InitializeComponent();
}
private void PointerPageShowDialog_PointerPressed(object? sender, PointerPressedEventArgs e)
{
void CaptureLost(object? sender, PointerCaptureLostEventArgs e)
{
PointerCaptureStatus.Text = "None";
((Control)sender!).PointerCaptureLost -= CaptureLost;
}
var window = TopLevel.GetTopLevel(this) as Window ??
throw new AvaloniaInternalException("PointerPage is not attached to a Window.");
var captured = e.Pointer.Captured as Control;
if (captured is not null)
{
captured.PointerCaptureLost += CaptureLost;
}
PointerCaptureStatus.Text = captured?.ToString() ?? "None";
var dialog = new Window
{
Width = 200,
Height = 200,
};
dialog.Content = new Button
{
Content = "Close",
Command = new DelegateCommand(() => dialog.Close()),
};
dialog.ShowDialog(window);
}
}

14
samples/IntegrationTestApp/Pages/RadioButtonPage.axaml

@ -0,0 +1,14 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.RadioButtonPage">
<StackPanel Orientation="Vertical">
<RadioButton Name="BasicRadioButton">Sample RadioButton</RadioButton>
<StackPanel Orientation="Vertical">
<RadioButton Name="ThreeStatesRadioButton1" IsChecked="True" IsThreeState="True">Three States: Option 1</RadioButton>
<RadioButton Name="ThreeStatesRadioButton2" IsChecked="False" IsThreeState="True">Three States: Option 2</RadioButton>
</StackPanel>
</StackPanel>
</UserControl>

11
samples/IntegrationTestApp/Pages/RadioButtonPage.axaml.cs

@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace IntegrationTestApp.Pages;
public partial class RadioButtonPage : UserControl
{
public RadioButtonPage()
{
InitializeComponent();
}
}

19
samples/IntegrationTestApp/Pages/ScreensPage.axaml

@ -0,0 +1,19 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.ScreensPage">
<StackPanel Spacing="4">
<Button Name="ScreenRefresh"
Content="Refresh"
Click="ScreenRefresh_Click"/>
<TextBox Name="ScreenName" Watermark="DisplayName" UseFloatingWatermark="true" />
<TextBox Name="ScreenHandle" Watermark="Handle" UseFloatingWatermark="true" />
<TextBox Name="ScreenScaling" Watermark="Scaling" UseFloatingWatermark="true" />
<TextBox Name="ScreenBounds" Watermark="Bounds" UseFloatingWatermark="true" />
<TextBox Name="ScreenWorkArea" Watermark="WorkArea" UseFloatingWatermark="true" />
<TextBox Name="ScreenOrientation" Watermark="Orientation" UseFloatingWatermark="true" />
<TextBox Name="ScreenSameReference" Watermark="Is same reference" UseFloatingWatermark="true" />
</StackPanel>
</UserControl>

32
samples/IntegrationTestApp/Pages/ScreensPage.axaml.cs

@ -0,0 +1,32 @@
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform;
namespace IntegrationTestApp.Pages;
public partial class ScreensPage : UserControl
{
private Screen? _lastScreen;
public ScreensPage()
{
InitializeComponent();
}
private void ScreenRefresh_Click(object? sender, RoutedEventArgs e)
{
var window = TopLevel.GetTopLevel(this) as Window ??
throw new AvaloniaInternalException("ScreensPage is not attached to a Window.");
var lastScreen = _lastScreen;
var screen = _lastScreen = window.Screens.ScreenFromWindow(window);
ScreenName.Text = screen?.DisplayName;
ScreenHandle.Text = screen?.TryGetPlatformHandle()?.ToString();
ScreenBounds.Text = screen?.Bounds.ToString();
ScreenWorkArea.Text = screen?.WorkingArea.ToString();
ScreenScaling.Text = screen?.Scaling.ToString(CultureInfo.InvariantCulture);
ScreenOrientation.Text = screen?.CurrentOrientation.ToString();
ScreenSameReference.Text = ReferenceEquals(lastScreen, screen).ToString();
}
}

8
samples/IntegrationTestApp/Pages/ScrollBarPage.axaml

@ -0,0 +1,8 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.ScrollBarPage">
<ScrollBar Name="MyScrollBar" Orientation="Horizontal" AllowAutoHide="False" Width="200" Height="30" Value="20"/>
</UserControl>

11
samples/IntegrationTestApp/Pages/ScrollBarPage.axaml.cs

@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace IntegrationTestApp.Pages;
public partial class ScrollBarPage : UserControl
{
public ScrollBarPage()
{
InitializeComponent();
}
}

17
samples/IntegrationTestApp/Pages/SliderPage.axaml

@ -0,0 +1,17 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.SliderPage">
<DockPanel>
<DockPanel DockPanel.Dock="Top">
<TextBox Name="HorizontalSliderValue"
DockPanel.Dock="Right"
Text="{Binding #HorizontalSlider.Value, Mode=OneWay, StringFormat=\{0:0\}}"
VerticalAlignment="Top"/>
<Slider Name="HorizontalSlider" Value="50"/>
</DockPanel>
<Button Name="ResetSliders" Click="ResetSliders_Click">Reset</Button>
</DockPanel>
</UserControl>

17
samples/IntegrationTestApp/Pages/SliderPage.axaml.cs

@ -0,0 +1,17 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace IntegrationTestApp.Pages;
public partial class SliderPage : UserControl
{
public SliderPage()
{
InitializeComponent();
}
private void ResetSliders_Click(object? sender, RoutedEventArgs e)
{
HorizontalSlider.Value = 50;
}
}

21
samples/IntegrationTestApp/Pages/WindowDecorationsPage.axaml

@ -0,0 +1,21 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.WindowDecorationsPage">
<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"
Click="ApplyWindowDecorations_Click"/>
<Button Name="ShowNewWindowDecorations"
Content="Show new Window with decorations"
Click="ShowNewWindowDecorations_Click"/>
<TextBox Name="WindowDecorationProperties" AcceptsReturn="True" />
</StackPanel>
</UserControl>

63
samples/IntegrationTestApp/Pages/WindowDecorationsPage.axaml.cs

@ -0,0 +1,63 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Platform;
namespace IntegrationTestApp.Pages;
public partial class WindowDecorationsPage : UserControl
{
public WindowDecorationsPage()
{
InitializeComponent();
}
private void SetWindowDecorations(Window window)
{
window.ExtendClientAreaToDecorationsHint = WindowExtendClientAreaToDecorationsHint.IsChecked!.Value;
window.ExtendClientAreaTitleBarHeightHint =
int.TryParse(WindowTitleBarHeightHint.Text, out var val) ? val / window.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 == Window.OffScreenMarginProperty || e.Property == Window.WindowDecorationMarginProperty)
{
AdjustOffsets(window);
}
}
void AdjustOffsets(Window window)
{
var scaling = window.DesktopScaling;
window.Padding = window.OffScreenMargin;
((Control)window.Content!).Margin = window.WindowDecorationMargin;
WindowDecorationProperties.Text =
$"{window.OffScreenMargin.Top * scaling} {window.WindowDecorationMargin.Top * scaling} {window.IsExtendedIntoWindowDecorations}";
}
}
private void ApplyWindowDecorations_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
var window = TopLevel.GetTopLevel(this) as Window ??
throw new AvaloniaInternalException("WindowDecorationsPage is not attached to a Window.");
SetWindowDecorations(window);
}
private void ShowNewWindowDecorations_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
var window = new ShowWindowTest();
SetWindowDecorations(window);
window.Show();
}
}

45
samples/IntegrationTestApp/Pages/WindowPage.axaml

@ -0,0 +1,45 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.Pages.WindowPage">
<Grid ColumnDefinitions="*,8,*">
<StackPanel Grid.Column="0">
<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>
<ComboBox Name="ShowWindowState" SelectedIndex="0">
<ComboBoxItem Name="ShowWindowStateNormal">Normal</ComboBoxItem>
<ComboBoxItem Name="ShowWindowStateMinimized">Minimized</ComboBoxItem>
<ComboBoxItem Name="ShowWindowStateMaximized">Maximized</ComboBoxItem>
<ComboBoxItem Name="ShowWindowStateFullScreen">FullScreen</ComboBoxItem>
</ComboBox>
<ComboBox Name="ShowWindowSystemDecorations" SelectedIndex="2">
<ComboBoxItem Name="ShowWindowSystemDecorationsNone">None</ComboBoxItem>
<ComboBoxItem Name="ShowWindowSystemDecorationsBorderOnly">BorderOnly</ComboBoxItem>
<ComboBoxItem Name="ShowWindowSystemDecorationsFull">Full</ComboBoxItem>
</ComboBox>
<CheckBox Name="ShowWindowExtendClientAreaToDecorationsHint">ExtendClientAreaToDecorationsHint</CheckBox>
<CheckBox Name="ShowWindowCanResize" IsChecked="True">Can Resize</CheckBox>
<Button Name="ShowWindow" Click="ShowWindow_Click">Show Window</Button>
<Button Name="SendToBack" Click="SendToBack_Click">Send to Back</Button>
<Button Name="EnterFullscreen" Click="EnterFullscreen_Click">Enter Fullscreen</Button>
<Button Name="ExitFullscreen" Click="ExitFullscreen_Click">Exit Fullscreen</Button>
<Button Name="RestoreAll" Click="RestoreAll_Click">Restore All</Button>
<Button Name="ShowTopmostWindow" Click="ShowTopmostWindow_Click">Show Topmost Window</Button>
</StackPanel>
<StackPanel Grid.Column="2">
<Button Name="ShowTransparentWindow" Click="ShowTransparentWindow_Click">Transparent Window</Button>
<Button Name="ShowTransparentPopup" Click="ShowTransparentPopup_Click">Transparent Popup</Button>
</StackPanel>
</Grid>
</UserControl>

199
samples/IntegrationTestApp/Pages/WindowPage.axaml.cs

@ -0,0 +1,199 @@
using System.Linq;
using Avalonia;
using Avalonia.Automation;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Interactivity;
using Avalonia.Media;
namespace IntegrationTestApp.Pages;
public partial class WindowPage : UserControl
{
public WindowPage()
{
InitializeComponent();
}
private Window Window => TopLevel.GetTopLevel(this) as Window ??
throw new AvaloniaInternalException("WindowPage is not attached to a Window.");
private void ShowWindow_Click(object? sender, RoutedEventArgs e)
{
var size = !string.IsNullOrWhiteSpace(ShowWindowSize.Text) ? Size.Parse(ShowWindowSize.Text) : (Size?)null;
var window = new ShowWindowTest
{
WindowStartupLocation = (WindowStartupLocation)ShowWindowLocation.SelectedIndex,
CanResize = ShowWindowCanResize.IsChecked ?? false,
};
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
{
// Make sure the windows have unique names and AutomationIds.
var existing = lifetime.Windows.OfType<ShowWindowTest>().Count();
if (existing > 0)
{
AutomationProperties.SetAutomationId(window, window.Name + (existing + 1));
window.Title += $" {existing + 1}";
}
}
if (size.HasValue)
{
window.Width = size.Value.Width;
window.Height = size.Value.Height;
}
ShowWindowSize.Text = string.Empty;
window.ExtendClientAreaToDecorationsHint = ShowWindowExtendClientAreaToDecorationsHint.IsChecked ?? false;
window.SystemDecorations = (SystemDecorations)ShowWindowSystemDecorations.SelectedIndex;
window.WindowState = (WindowState)ShowWindowState.SelectedIndex;
switch (ShowWindowMode.SelectedIndex)
{
case 0:
window.Show();
break;
case 1:
window.Show(Window);
break;
case 2:
window.ShowDialog(Window);
break;
}
}
private void ShowTransparentWindow_Click(object? sender, RoutedEventArgs e)
{
// Show a background window to make sure the color behind the transparent window is
// a known color (green).
var backgroundWindow = new Window
{
Title = "Transparent Window Background",
Name = "TransparentWindowBackground",
Width = 300,
Height = 300,
Background = Brushes.Green,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
// This is the transparent window with a red circle.
var window = new Window
{
Title = "Transparent Window",
Name = "TransparentWindow",
SystemDecorations = SystemDecorations.None,
Background = Brushes.Transparent,
TransparencyLevelHint = new[] { WindowTransparencyLevel.Transparent },
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Width = 200,
Height = 200,
Content = new Border
{
Background = Brushes.Red,
CornerRadius = new CornerRadius(100),
}
};
window.PointerPressed += (_, _) =>
{
window.Close();
backgroundWindow.Close();
};
backgroundWindow.Show(Window);
window.Show(backgroundWindow);
}
private void ShowTransparentPopup_Click(object? sender, RoutedEventArgs e)
{
var popup = new Popup
{
WindowManagerAddShadowHint = false,
Placement = PlacementMode.AnchorAndGravity,
PlacementAnchor = PopupAnchor.Top,
PlacementGravity = PopupGravity.Bottom,
Width = 200,
Height = 200,
Child = new Border
{
Background = Brushes.Red,
CornerRadius = new CornerRadius(100),
}
};
// Show a background window to make sure the color behind the transparent window is
// a known color (green).
var backgroundWindow = new Window
{
Title = "Transparent Popup Background",
Name = "TransparentPopupBackground",
Width = 200,
Height = 200,
Background = Brushes.Green,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Content = new Border
{
Name = "PopupContainer",
Child = popup,
[AutomationProperties.AccessibilityViewProperty] = AccessibilityView.Content,
}
};
backgroundWindow.PointerPressed += (_, _) => backgroundWindow.Close();
backgroundWindow.Show(Window);
popup.Open();
}
private void SendToBack_Click(object? sender, RoutedEventArgs e)
{
var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
foreach (var window in lifetime.Windows.ToArray())
{
window.Activate();
}
}
private void EnterFullscreen_Click(object? sender, RoutedEventArgs e)
{
Window.WindowState = WindowState.FullScreen;
}
private void ExitFullscreen_Click(object? sender, RoutedEventArgs e)
{
Window.WindowState = WindowState.Normal;
}
private void RestoreAll_Click(object? sender, RoutedEventArgs e)
{
var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
foreach (var window in lifetime.Windows.ToArray())
{
window.Show();
if (window.WindowState == WindowState.Minimized)
window.WindowState = WindowState.Normal;
}
}
private void ShowTopmostWindow_Click(object? sender, RoutedEventArgs e)
{
var mainWindow = new TopmostWindowTest("OwnerWindow")
{
Topmost = true,
Title = "Owner Window"
};
var ownedWindow = new TopmostWindowTest("OwnedWindow")
{
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Title = "Owned Window"
};
mainWindow.Show();
ownedWindow.Show(mainWindow);
}
}

1
samples/IntegrationTestApp/ShowWindowTest.axaml.cs

@ -2,7 +2,6 @@ using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Threading; using Avalonia.Threading;
namespace IntegrationTestApp namespace IntegrationTestApp

23
samples/IntegrationTestApp/ViewModels/MainWindowViewModel.cs

@ -0,0 +1,23 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using IntegrationTestApp.Models;
namespace IntegrationTestApp.ViewModels;
internal class MainWindowViewModel : ViewModelBase
{
private Page? _selectedPage;
public MainWindowViewModel(IEnumerable<Page> pages)
{
Pages = new(pages);
}
public ObservableCollection<Page> Pages { get; }
public Page? SelectedPage
{
get => _selectedPage;
set => RaiseAndSetIfChanged(ref _selectedPage, value);
}
}

24
samples/IntegrationTestApp/ViewModels/ViewModelBase.cs

@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace IntegrationTestApp.ViewModels;
internal class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
RaisePropertyChanged(propertyName);
return true;
}
return false;
}
protected void RaisePropertyChanged([CallerMemberName] string? propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

2
src/Avalonia.Controls.DataGrid/DataGridCell.cs

@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Automation;
using Avalonia.Automation.Peers; using Avalonia.Automation.Peers;
using Avalonia.Controls.Automation.Peers; using Avalonia.Controls.Automation.Peers;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
@ -37,6 +38,7 @@ namespace Avalonia.Controls
(x,e) => x.DataGridCell_PointerPressed(e), handledEventsToo: true); (x,e) => x.DataGridCell_PointerPressed(e), handledEventsToo: true);
FocusableProperty.OverrideDefaultValue<DataGridCell>(true); FocusableProperty.OverrideDefaultValue<DataGridCell>(true);
IsTabStopProperty.OverrideDefaultValue<DataGridCell>(false); IsTabStopProperty.OverrideDefaultValue<DataGridCell>(false);
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<DataGridCell>(IsOffscreenBehavior.FromClip);
} }
public DataGridCell() public DataGridCell()
{ } { }

2
src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs

@ -6,6 +6,7 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using Avalonia.Automation;
using Avalonia.Automation.Peers; using Avalonia.Automation.Peers;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Automation.Peers; using Avalonia.Controls.Automation.Peers;
@ -74,6 +75,7 @@ namespace Avalonia.Controls
AreSeparatorsVisibleProperty.Changed.AddClassHandler<DataGridColumnHeader>((x, e) => x.OnAreSeparatorsVisibleChanged(e)); AreSeparatorsVisibleProperty.Changed.AddClassHandler<DataGridColumnHeader>((x, e) => x.OnAreSeparatorsVisibleChanged(e));
PressedMixin.Attach<DataGridColumnHeader>(); PressedMixin.Attach<DataGridColumnHeader>();
IsTabStopProperty.OverrideDefaultValue<DataGridColumnHeader>(false); IsTabStopProperty.OverrideDefaultValue<DataGridColumnHeader>(false);
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<DataGridColumnHeader>(IsOffscreenBehavior.FromClip);
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls.DataGrid/DataGridRow.cs

@ -17,6 +17,7 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using Avalonia.Automation.Peers; using Avalonia.Automation.Peers;
using Avalonia.Reactive; using Avalonia.Reactive;
using Avalonia.Automation;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -143,6 +144,7 @@ namespace Avalonia.Controls
AreDetailsVisibleProperty.Changed.AddClassHandler<DataGridRow>((x, e) => x.OnAreDetailsVisibleChanged(e)); AreDetailsVisibleProperty.Changed.AddClassHandler<DataGridRow>((x, e) => x.OnAreDetailsVisibleChanged(e));
PointerPressedEvent.AddClassHandler<DataGridRow>((x, e) => x.DataGridRow_PointerPressed(e), handledEventsToo: true); PointerPressedEvent.AddClassHandler<DataGridRow>((x, e) => x.DataGridRow_PointerPressed(e), handledEventsToo: true);
IsTabStopProperty.OverrideDefaultValue<DataGridRow>(false); IsTabStopProperty.OverrideDefaultValue<DataGridRow>(false);
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<DataGridRow>(IsOffscreenBehavior.FromClip);
} }
/// <summary> /// <summary>

6
src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs

@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Automation;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Media; using Avalonia.Media;
@ -50,6 +51,11 @@ namespace Avalonia.Controls.Primitives
AddHandler(PointerPressedEvent, DataGridRowHeader_PointerPressed, handledEventsToo: true); AddHandler(PointerPressedEvent, DataGridRowHeader_PointerPressed, handledEventsToo: true);
} }
static DataGridRowHeader()
{
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<DataGridRowHeader>(IsOffscreenBehavior.FromClip);
}
internal Control Owner internal Control Owner
{ {
get; get;

12
src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs

@ -153,6 +153,17 @@ namespace Avalonia.Automation.Peers
/// <returns></returns> /// <returns></returns>
public bool IsKeyboardFocusable() => IsKeyboardFocusableCore(); public bool IsKeyboardFocusable() => IsKeyboardFocusableCore();
/// <summary>
/// Gets a value that indicates whether an element is off the screen.
/// </summary>
/// <remarks>
/// This property does not indicate whether the element is visible. In some circumstances,
/// an element is on the screen but is still not visible. For example, if the element is
/// on the screen but obscured by other elements, it might not be visible. In this case,
/// the method returns false.
/// </remarks>
public bool IsOffscreen() => IsOffscreenCore();
/// <summary> /// <summary>
/// Sets the keyboard focus on the element that is associated with this automation peer. /// Sets the keyboard focus on the element that is associated with this automation peer.
/// </summary> /// </summary>
@ -245,6 +256,7 @@ namespace Avalonia.Automation.Peers
protected abstract bool IsControlElementCore(); protected abstract bool IsControlElementCore();
protected abstract bool IsEnabledCore(); protected abstract bool IsEnabledCore();
protected abstract bool IsKeyboardFocusableCore(); protected abstract bool IsKeyboardFocusableCore();
protected virtual bool IsOffscreenCore() => false;
protected abstract void SetFocusCore(); protected abstract void SetFocusCore();
protected abstract bool ShowContextMenuCore(); protected abstract bool ShowContextMenuCore();

14
src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Utilities;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia.Automation.Peers namespace Avalonia.Automation.Peers
@ -201,6 +202,19 @@ namespace Avalonia.Automation.Peers
return view == AccessibilityView.Default ? IsControlElementCore() : view >= AccessibilityView.Control; return view == AccessibilityView.Default ? IsControlElementCore() : view >= AccessibilityView.Control;
} }
protected override bool IsOffscreenCore()
{
return AutomationProperties.GetIsOffscreenBehavior(Owner) switch
{
IsOffscreenBehavior.Onscreen => false,
IsOffscreenBehavior.Offscreen => true,
IsOffscreenBehavior.FromClip => Owner.GetTransformedBounds() is not { } bounds ||
MathUtilities.IsZero(bounds.Clip.Width) ||
MathUtilities.IsZero(bounds.Clip.Height),
_ => !Owner.IsVisible,
};
}
private static Rect GetBounds(Control control) private static Rect GetBounds(Control control)
{ {
var root = control.GetVisualRoot(); var root = control.GetVisualRoot();

2
src/Avalonia.Controls/ListBoxItem.cs

@ -1,3 +1,4 @@
using Avalonia.Automation;
using Avalonia.Automation.Peers; using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
@ -30,6 +31,7 @@ namespace Avalonia.Controls
SelectableMixin.Attach<ListBoxItem>(IsSelectedProperty); SelectableMixin.Attach<ListBoxItem>(IsSelectedProperty);
PressedMixin.Attach<ListBoxItem>(); PressedMixin.Attach<ListBoxItem>();
FocusableProperty.OverrideDefaultValue<ListBoxItem>(true); FocusableProperty.OverrideDefaultValue<ListBoxItem>(true);
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<ListBoxItem>(IsOffscreenBehavior.FromClip);
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/MenuItem.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Automation;
using Avalonia.Automation.Peers; using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
@ -141,6 +142,7 @@ namespace Avalonia.Controls
ItemsPanelProperty.OverrideDefaultValue<MenuItem>(DefaultPanel); ItemsPanelProperty.OverrideDefaultValue<MenuItem>(DefaultPanel);
ClickEvent.AddClassHandler<MenuItem>((x, e) => x.OnClick(e)); ClickEvent.AddClassHandler<MenuItem>((x, e) => x.OnClick(e));
SubmenuOpenedEvent.AddClassHandler<MenuItem>((x, e) => x.OnSubmenuOpened(e)); SubmenuOpenedEvent.AddClassHandler<MenuItem>((x, e) => x.OnSubmenuOpened(e));
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<MenuItem>(IsOffscreenBehavior.FromClip);
} }
public MenuItem() public MenuItem()

1
src/Avalonia.Controls/TabItem.cs

@ -39,6 +39,7 @@ namespace Avalonia.Controls
FocusableProperty.OverrideDefaultValue(typeof(TabItem), true); FocusableProperty.OverrideDefaultValue(typeof(TabItem), true);
DataContextProperty.Changed.AddClassHandler<TabItem>((x, e) => x.UpdateHeader(e)); DataContextProperty.Changed.AddClassHandler<TabItem>((x, e) => x.UpdateHeader(e));
AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue<TabItem>(AutomationControlType.TabItem); AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue<TabItem>(AutomationControlType.TabItem);
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<TabItem>(IsOffscreenBehavior.FromClip);
AccessKeyHandler.AccessKeyPressedEvent.AddClassHandler<TabItem>((tabItem, args) => tabItem.TabItemActivated(args)); AccessKeyHandler.AccessKeyPressedEvent.AddClassHandler<TabItem>((tabItem, args) => tabItem.TabItemActivated(args));
} }

3
src/Avalonia.Controls/TreeViewItem.cs

@ -2,6 +2,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Linq; using System.Linq;
using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
@ -61,6 +63,7 @@ namespace Avalonia.Controls
PressedMixin.Attach<TreeViewItem>(); PressedMixin.Attach<TreeViewItem>();
FocusableProperty.OverrideDefaultValue<TreeViewItem>(true); FocusableProperty.OverrideDefaultValue<TreeViewItem>(true);
ItemsPanelProperty.OverrideDefaultValue<TreeViewItem>(DefaultPanel); ItemsPanelProperty.OverrideDefaultValue<TreeViewItem>(DefaultPanel);
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<TreeViewItem>(IsOffscreenBehavior.FromClip);
RequestBringIntoViewEvent.AddClassHandler<TreeViewItem>((x, e) => x.OnRequestBringIntoView(e)); RequestBringIntoViewEvent.AddClassHandler<TreeViewItem>((x, e) => x.OnRequestBringIntoView(e));
} }

1
src/Windows/Avalonia.Win32/Automation/AutomationNode.cs

@ -118,6 +118,7 @@ namespace Avalonia.Win32.Automation
UiaPropertyId.IsControlElement => InvokeSync(() => Peer.IsControlElement()), UiaPropertyId.IsControlElement => InvokeSync(() => Peer.IsControlElement()),
UiaPropertyId.IsEnabled => InvokeSync(() => Peer.IsEnabled()), UiaPropertyId.IsEnabled => InvokeSync(() => Peer.IsEnabled()),
UiaPropertyId.IsKeyboardFocusable => InvokeSync(() => Peer.IsKeyboardFocusable()), UiaPropertyId.IsKeyboardFocusable => InvokeSync(() => Peer.IsKeyboardFocusable()),
UiaPropertyId.IsOffscreen => InvokeSync(() => Peer.IsOffscreen()),
UiaPropertyId.LocalizedControlType => InvokeSync(() => Peer.GetLocalizedControlType()), UiaPropertyId.LocalizedControlType => InvokeSync(() => Peer.GetLocalizedControlType()),
UiaPropertyId.Name => InvokeSync(() => Peer.GetName()), UiaPropertyId.Name => InvokeSync(() => Peer.GetName()),
UiaPropertyId.ProcessId => Process.GetCurrentProcess().Id, UiaPropertyId.ProcessId => Process.GetCurrentProcess().Id,

21
tests/Avalonia.IntegrationTests.Appium/AutomationTests.cs

@ -1,20 +1,13 @@
using OpenQA.Selenium.Appium; using Xunit;
using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public class AutomationTests public class AutomationTests : TestBase
{ {
private readonly AppiumDriver _session;
public AutomationTests(DefaultAppFixture fixture) public AutomationTests(DefaultAppFixture fixture)
: base(fixture, "Automation")
{ {
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("Automation");
tab.Click();
} }
[Fact] [Fact]
@ -22,15 +15,15 @@ namespace Avalonia.IntegrationTests.Appium
{ {
// AutomationID can be specified by the Name or AutomationProperties.AutomationId // AutomationID can be specified by the Name or AutomationProperties.AutomationId
// properties, with the latter taking precedence. // properties, with the latter taking precedence.
var byName = _session.FindElementByAccessibilityId("TextBlockWithName"); var byName = Session.FindElementByAccessibilityId("TextBlockWithName");
var byAutomationId = _session.FindElementByAccessibilityId("TextBlockWithNameAndAutomationId"); var byAutomationId = Session.FindElementByAccessibilityId("TextBlockWithNameAndAutomationId");
} }
[Fact] [Fact]
public void LabeledBy() public void LabeledBy()
{ {
var label = _session.FindElementByAccessibilityId("TextBlockAsLabel"); var label = Session.FindElementByAccessibilityId("TextBlockAsLabel");
var labeledTextBox = _session.FindElementByAccessibilityId("LabeledByTextBox"); var labeledTextBox = Session.FindElementByAccessibilityId("LabeledByTextBox");
Assert.Equal("Label for TextBox", label.Text); Assert.Equal("Label for TextBox", label.Text);
Assert.Equal("Label for TextBox", labeledTextBox.GetName()); Assert.Equal("Label for TextBox", labeledTextBox.GetName());

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

@ -1,27 +1,19 @@
using System.Runtime.InteropServices; using Xunit;
using OpenQA.Selenium.Appium;
using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public class ButtonTests public class ButtonTests : TestBase
{ {
private readonly AppiumDriver _session;
public ButtonTests(DefaultAppFixture fixture) public ButtonTests(DefaultAppFixture fixture)
: base(fixture, "Button")
{ {
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("Button");
tab.Click();
} }
[Fact] [Fact]
public void DisabledButton() public void DisabledButton()
{ {
var button = _session.FindElementByAccessibilityId("DisabledButton"); var button = Session.FindElementByAccessibilityId("DisabledButton");
Assert.Equal("Disabled Button", button.Text); Assert.Equal("Disabled Button", button.Text);
Assert.False(button.Enabled); Assert.False(button.Enabled);
@ -30,7 +22,7 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void EffectivelyDisabledButton() public void EffectivelyDisabledButton()
{ {
var button = _session.FindElementByAccessibilityId("EffectivelyDisabledButton"); var button = Session.FindElementByAccessibilityId("EffectivelyDisabledButton");
Assert.Equal("Effectively Disabled Button", button.Text); Assert.Equal("Effectively Disabled Button", button.Text);
Assert.False(button.Enabled); Assert.False(button.Enabled);
@ -39,7 +31,7 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void BasicButton() public void BasicButton()
{ {
var button = _session.FindElementByAccessibilityId("BasicButton"); var button = Session.FindElementByAccessibilityId("BasicButton");
Assert.Equal("Basic Button", button.Text); Assert.Equal("Basic Button", button.Text);
Assert.True(button.Enabled); Assert.True(button.Enabled);
@ -48,7 +40,7 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void ButtonWithTextBlock() public void ButtonWithTextBlock()
{ {
var button = _session.FindElementByAccessibilityId("ButtonWithTextBlock"); var button = Session.FindElementByAccessibilityId("ButtonWithTextBlock");
Assert.Equal("Button with TextBlock", button.Text); Assert.Equal("Button with TextBlock", button.Text);
} }
@ -56,7 +48,7 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.Windows)] [PlatformFact(TestPlatforms.Windows)]
public void ButtonWithAcceleratorKey() public void ButtonWithAcceleratorKey()
{ {
var button = _session.FindElementByAccessibilityId("ButtonWithAcceleratorKey"); var button = Session.FindElementByAccessibilityId("ButtonWithAcceleratorKey");
Assert.Equal("Ctrl+B", button.GetAttribute("AcceleratorKey")); Assert.Equal("Ctrl+B", button.GetAttribute("AcceleratorKey"));
} }

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

@ -1,26 +1,19 @@
using OpenQA.Selenium.Appium; using Xunit;
using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public class CheckBoxTests public class CheckBoxTests : TestBase
{ {
private readonly AppiumDriver _session;
public CheckBoxTests(DefaultAppFixture fixture) public CheckBoxTests(DefaultAppFixture fixture)
: base(fixture, "CheckBox")
{ {
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("CheckBox");
tab.Click();
} }
[Fact] [Fact]
public void UncheckedCheckBox() public void UncheckedCheckBox()
{ {
var checkBox = _session.FindElementByAccessibilityId("UncheckedCheckBox"); var checkBox = Session.FindElementByAccessibilityId("UncheckedCheckBox");
Assert.Equal("Unchecked", checkBox.GetName()); Assert.Equal("Unchecked", checkBox.GetName());
Assert.Equal(false, checkBox.GetIsChecked()); Assert.Equal(false, checkBox.GetIsChecked());
@ -32,7 +25,7 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void CheckedCheckBox() public void CheckedCheckBox()
{ {
var checkBox = _session.FindElementByAccessibilityId("CheckedCheckBox"); var checkBox = Session.FindElementByAccessibilityId("CheckedCheckBox");
Assert.Equal("Checked", checkBox.GetName()); Assert.Equal("Checked", checkBox.GetName());
Assert.Equal(true, checkBox.GetIsChecked()); Assert.Equal(true, checkBox.GetIsChecked());
@ -44,7 +37,7 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void ThreeStateCheckBox() public void ThreeStateCheckBox()
{ {
var checkBox = _session.FindElementByAccessibilityId("ThreeStateCheckBox"); var checkBox = Session.FindElementByAccessibilityId("ThreeStateCheckBox");
Assert.Equal("ThreeState", checkBox.GetName()); Assert.Equal("ThreeState", checkBox.GetName());
Assert.Null(checkBox.GetIsChecked()); Assert.Null(checkBox.GetIsChecked());

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

@ -1,32 +1,25 @@
using OpenQA.Selenium; using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using Xunit; using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
public abstract class ComboBoxTests public abstract class ComboBoxTests : TestBase
{ {
private readonly AppiumDriver _session;
public ComboBoxTests(DefaultAppFixture fixture) public ComboBoxTests(DefaultAppFixture fixture)
: base(fixture, "ComboBox")
{ {
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("ComboBox");
tab.Click();
} }
[Fact] [Fact]
public void Can_Change_Selection_Using_Mouse() public void Can_Change_Selection_Using_Mouse()
{ {
var comboBox = _session.FindElementByAccessibilityId("BasicComboBox"); var comboBox = Session.FindElementByAccessibilityId("BasicComboBox");
_session.FindElementByAccessibilityId("ComboBoxSelectFirst").Click(); Session.FindElementByAccessibilityId("ComboBoxSelectFirst").Click();
Assert.Equal("Item 0", comboBox.GetComboBoxValue()); Assert.Equal("Item 0", comboBox.GetComboBoxValue());
comboBox.Click(); comboBox.Click();
_session.FindElementByName("Item 1").SendClick(); Session.FindElementByName("Item 1").SendClick();
Assert.Equal("Item 1", comboBox.GetComboBoxValue()); Assert.Equal("Item 1", comboBox.GetComboBoxValue());
} }
@ -34,13 +27,13 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void Can_Change_Selection_From_Unselected_Using_Mouse() public void Can_Change_Selection_From_Unselected_Using_Mouse()
{ {
var comboBox = _session.FindElementByAccessibilityId("BasicComboBox"); var comboBox = Session.FindElementByAccessibilityId("BasicComboBox");
_session.FindElementByAccessibilityId("ComboBoxSelectionClear").Click(); Session.FindElementByAccessibilityId("ComboBoxSelectionClear").Click();
Assert.Equal(string.Empty, comboBox.GetComboBoxValue()); Assert.Equal(string.Empty, comboBox.GetComboBoxValue());
comboBox.Click(); comboBox.Click();
_session.FindElementByName("Item 0").SendClick(); Session.FindElementByName("Item 0").SendClick();
Assert.Equal("Item 0", comboBox.GetComboBoxValue()); Assert.Equal("Item 0", comboBox.GetComboBoxValue());
} }
@ -48,13 +41,13 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.Windows)] [PlatformFact(TestPlatforms.Windows)]
public void Can_Change_Selection_With_Keyboard_When_Closed() public void Can_Change_Selection_With_Keyboard_When_Closed()
{ {
var comboBox = _session.FindElementByAccessibilityId("BasicComboBox"); var comboBox = Session.FindElementByAccessibilityId("BasicComboBox");
var wrap = _session.FindElementByAccessibilityId("ComboBoxWrapSelection"); var wrap = Session.FindElementByAccessibilityId("ComboBoxWrapSelection");
if (wrap.GetIsChecked() != false) if (wrap.GetIsChecked() != false)
wrap.Click(); wrap.Click();
_session.FindElementByAccessibilityId("ComboBoxSelectionClear").Click(); Session.FindElementByAccessibilityId("ComboBoxSelectionClear").Click();
comboBox.SendKeys(Keys.ArrowDown); comboBox.SendKeys(Keys.ArrowDown);
Assert.Equal("Item 0", comboBox.GetComboBoxValue()); Assert.Equal("Item 0", comboBox.GetComboBoxValue());
@ -75,13 +68,13 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.Windows)] [PlatformFact(TestPlatforms.Windows)]
public void Can_Change_Wrapping_Selection_With_Keyboard_When_Closed() public void Can_Change_Wrapping_Selection_With_Keyboard_When_Closed()
{ {
var comboBox = _session.FindElementByAccessibilityId("BasicComboBox"); var comboBox = Session.FindElementByAccessibilityId("BasicComboBox");
var wrap = _session.FindElementByAccessibilityId("ComboBoxWrapSelection"); var wrap = Session.FindElementByAccessibilityId("ComboBoxWrapSelection");
if (wrap.GetIsChecked() != true) if (wrap.GetIsChecked() != true)
wrap.Click(); wrap.Click();
_session.FindElementByAccessibilityId("ComboBoxSelectionClear").Click(); Session.FindElementByAccessibilityId("ComboBoxSelectionClear").Click();
comboBox.SendKeys(Keys.ArrowDown); comboBox.SendKeys(Keys.ArrowDown);
Assert.Equal("Item 0", comboBox.GetComboBoxValue()); Assert.Equal("Item 0", comboBox.GetComboBoxValue());
@ -105,15 +98,15 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.Windows)] [PlatformFact(TestPlatforms.Windows)]
public void Can_Change_Selection_When_Open_With_Keyboard() public void Can_Change_Selection_When_Open_With_Keyboard()
{ {
var comboBox = _session.FindElementByAccessibilityId("BasicComboBox"); var comboBox = Session.FindElementByAccessibilityId("BasicComboBox");
_session.FindElementByAccessibilityId("ComboBoxSelectFirst").Click(); Session.FindElementByAccessibilityId("ComboBoxSelectFirst").Click();
Assert.Equal("Item 0", comboBox.GetComboBoxValue()); Assert.Equal("Item 0", comboBox.GetComboBoxValue());
comboBox.SendKeys(Keys.LeftAlt + Keys.ArrowDown); comboBox.SendKeys(Keys.LeftAlt + Keys.ArrowDown);
comboBox.SendKeys(Keys.ArrowDown); comboBox.SendKeys(Keys.ArrowDown);
var item = _session.FindElementByName("Item 1"); var item = Session.FindElementByName("Item 1");
item.SendKeys(Keys.Enter); item.SendKeys(Keys.Enter);
Assert.Equal("Item 1", comboBox.GetComboBoxValue()); Assert.Equal("Item 1", comboBox.GetComboBoxValue());
@ -122,15 +115,15 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.Windows)] [PlatformFact(TestPlatforms.Windows)]
public void Can_Change_Selection_When_Open_With_Keyboard_From_Unselected() public void Can_Change_Selection_When_Open_With_Keyboard_From_Unselected()
{ {
var comboBox = _session.FindElementByAccessibilityId("BasicComboBox"); var comboBox = Session.FindElementByAccessibilityId("BasicComboBox");
_session.FindElementByAccessibilityId("ComboBoxSelectionClear").Click(); Session.FindElementByAccessibilityId("ComboBoxSelectionClear").Click();
Assert.Equal(string.Empty, comboBox.GetComboBoxValue()); Assert.Equal(string.Empty, comboBox.GetComboBoxValue());
comboBox.SendKeys(Keys.LeftAlt + Keys.ArrowDown); comboBox.SendKeys(Keys.LeftAlt + Keys.ArrowDown);
comboBox.SendKeys(Keys.ArrowDown); comboBox.SendKeys(Keys.ArrowDown);
var item = _session.FindElementByName("Item 0"); var item = Session.FindElementByName("Item 0");
item.SendKeys(Keys.Enter); item.SendKeys(Keys.Enter);
Assert.Equal("Item 0", comboBox.GetComboBoxValue()); Assert.Equal("Item 0", comboBox.GetComboBoxValue());
@ -139,15 +132,15 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.Windows)] [PlatformFact(TestPlatforms.Windows)]
public void Can_Cancel_Keyboard_Selection_With_Escape() public void Can_Cancel_Keyboard_Selection_With_Escape()
{ {
var comboBox = _session.FindElementByAccessibilityId("BasicComboBox"); var comboBox = Session.FindElementByAccessibilityId("BasicComboBox");
_session.FindElementByAccessibilityId("ComboBoxSelectionClear").Click(); Session.FindElementByAccessibilityId("ComboBoxSelectionClear").Click();
Assert.Equal(string.Empty, comboBox.GetComboBoxValue()); Assert.Equal(string.Empty, comboBox.GetComboBoxValue());
comboBox.SendKeys(Keys.LeftAlt + Keys.ArrowDown); comboBox.SendKeys(Keys.LeftAlt + Keys.ArrowDown);
comboBox.SendKeys(Keys.ArrowDown); comboBox.SendKeys(Keys.ArrowDown);
var item = _session.FindElementByName("Item 0"); var item = Session.FindElementByName("Item 0");
item.SendKeys(Keys.Escape); item.SendKeys(Keys.Escape);
Assert.Equal(string.Empty, comboBox.GetComboBoxValue()); Assert.Equal(string.Empty, comboBox.GetComboBoxValue());

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

@ -6,30 +6,24 @@ using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public class ContextMenuTests public class ContextMenuTests : TestBase
{ {
private readonly AppiumDriver<AppiumWebElement> _session;
public ContextMenuTests(DefaultAppFixture fixture) public ContextMenuTests(DefaultAppFixture fixture)
: base(fixture, "ContextMenu")
{ {
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("ContextMenu");
tab.Click();
} }
[PlatformFact(TestPlatforms.Windows)] [PlatformFact(TestPlatforms.Windows)]
public void Select_First_Item_With_Down_Arrow_Key() public void Select_First_Item_With_Down_Arrow_Key()
{ {
var control = _session.FindElementByAccessibilityId("ShowContextMenu"); var control = Session.FindElementByAccessibilityId("ShowContextMenu");
new Actions(_session) new Actions(Session)
.ContextClick(control) .ContextClick(control)
.SendKeys(Keys.ArrowDown) .SendKeys(Keys.ArrowDown)
.Perform(); .Perform();
var clickedMenuItem = _session.FindElementByAccessibilityId("ContextMenuItem1"); var clickedMenuItem = Session.FindElementByAccessibilityId("ContextMenuItem1");
Assert.True(clickedMenuItem.GetIsFocused()); Assert.True(clickedMenuItem.GetIsFocused());
} }
} }

81
tests/Avalonia.IntegrationTests.Appium/GestureTests.cs

@ -1,34 +1,27 @@
using System; using System;
using System.Threading; using System.Threading;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Interactions; using OpenQA.Selenium.Interactions;
using Xunit; using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public class GestureTests public class GestureTests : TestBase
{ {
private readonly AppiumDriver _session;
public GestureTests(DefaultAppFixture fixture) public GestureTests(DefaultAppFixture fixture)
: base(fixture, "Gestures")
{ {
_session = fixture.Session; var clear = Session.FindElementByAccessibilityId("ResetGestures");
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("Gestures");
tab.Click();
var clear = _session.FindElementByAccessibilityId("ResetGestures");
clear.Click(); clear.Click();
} }
[Fact] [Fact]
public void Tapped_Is_Raised() public void Tapped_Is_Raised()
{ {
var border = _session.FindElementByAccessibilityId("GestureBorder"); var border = Session.FindElementByAccessibilityId("GestureBorder");
var lastGesture = _session.FindElementByAccessibilityId("LastGesture"); var lastGesture = Session.FindElementByAccessibilityId("LastGesture");
new Actions(_session).Click(border).Perform(); new Actions(Session).Click(border).Perform();
Assert.Equal("Tapped", lastGesture.Text); Assert.Equal("Tapped", lastGesture.Text);
} }
@ -36,14 +29,14 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void Tapped_Is_Raised_Slow() public void Tapped_Is_Raised_Slow()
{ {
var border = _session.FindElementByAccessibilityId("GestureBorder"); var border = Session.FindElementByAccessibilityId("GestureBorder");
var lastGesture = _session.FindElementByAccessibilityId("LastGesture"); var lastGesture = Session.FindElementByAccessibilityId("LastGesture");
new Actions(_session).ClickAndHold(border).Perform(); new Actions(Session).ClickAndHold(border).Perform();
Thread.Sleep(2000); Thread.Sleep(2000);
new Actions(_session).Release(border).Perform(); new Actions(Session).Release(border).Perform();
Assert.Equal("Tapped", lastGesture.Text); Assert.Equal("Tapped", lastGesture.Text);
} }
@ -51,10 +44,10 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void Tapped_Is_Not_Raised_For_Drag() public void Tapped_Is_Not_Raised_For_Drag()
{ {
var border = _session.FindElementByAccessibilityId("GestureBorder"); var border = Session.FindElementByAccessibilityId("GestureBorder");
var lastGesture = _session.FindElementByAccessibilityId("LastGesture"); var lastGesture = Session.FindElementByAccessibilityId("LastGesture");
new Actions(_session) new Actions(Session)
.ClickAndHold(border) .ClickAndHold(border)
.MoveByOffset(50, 50) .MoveByOffset(50, 50)
.Release() .Release()
@ -66,10 +59,10 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void DoubleTapped_Is_Raised() public void DoubleTapped_Is_Raised()
{ {
var border = _session.FindElementByAccessibilityId("GestureBorder"); var border = Session.FindElementByAccessibilityId("GestureBorder");
var lastGesture = _session.FindElementByAccessibilityId("LastGesture"); var lastGesture = Session.FindElementByAccessibilityId("LastGesture");
new Actions(_session).DoubleClick(border).Perform(); new Actions(Session).DoubleClick(border).Perform();
Assert.Equal("DoubleTapped", lastGesture.Text); Assert.Equal("DoubleTapped", lastGesture.Text);
} }
@ -77,15 +70,15 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.Windows | TestPlatforms.Linux)] [PlatformFact(TestPlatforms.Windows | TestPlatforms.Linux)]
public void DoubleTapped_Is_Raised_2() public void DoubleTapped_Is_Raised_2()
{ {
var border = _session.FindElementByAccessibilityId("GestureBorder"); var border = Session.FindElementByAccessibilityId("GestureBorder");
var lastGesture = _session.FindElementByAccessibilityId("LastGesture"); var lastGesture = Session.FindElementByAccessibilityId("LastGesture");
new Actions(_session).ClickAndHold(border).Release().Perform(); new Actions(Session).ClickAndHold(border).Release().Perform();
Thread.Sleep(50); Thread.Sleep(50);
// DoubleTapped is raised on second pointer press, not release. // DoubleTapped is raised on second pointer press, not release.
new Actions(_session).ClickAndHold(border).Perform(); new Actions(Session).ClickAndHold(border).Perform();
try try
{ {
@ -94,21 +87,21 @@ namespace Avalonia.IntegrationTests.Appium
finally finally
{ {
new Actions(_session).MoveToElement(lastGesture).Release().Perform(); new Actions(Session).MoveToElement(lastGesture).Release().Perform();
} }
} }
[Fact] [Fact]
public void DoubleTapped_Is_Raised_Not_Raised_If_Too_Slow() public void DoubleTapped_Is_Raised_Not_Raised_If_Too_Slow()
{ {
var border = _session.FindElementByAccessibilityId("GestureBorder"); var border = Session.FindElementByAccessibilityId("GestureBorder");
var lastGesture = _session.FindElementByAccessibilityId("LastGesture"); var lastGesture = Session.FindElementByAccessibilityId("LastGesture");
new Actions(_session).ClickAndHold(border).Release().Perform(); new Actions(Session).ClickAndHold(border).Release().Perform();
Thread.Sleep(2000); Thread.Sleep(2000);
new Actions(_session).ClickAndHold(border).Release().Perform(); new Actions(Session).ClickAndHold(border).Release().Perform();
Assert.Equal("Tapped", lastGesture.Text); Assert.Equal("Tapped", lastGesture.Text);
} }
@ -117,17 +110,17 @@ namespace Avalonia.IntegrationTests.Appium
public void DoubleTapped_Is_Raised_After_Control_Changes() public void DoubleTapped_Is_Raised_After_Control_Changes()
{ {
// #8733 // #8733
var border = _session.FindElementByAccessibilityId("GestureBorder"); var border = Session.FindElementByAccessibilityId("GestureBorder");
var lastGesture = _session.FindElementByAccessibilityId("LastGesture"); var lastGesture = Session.FindElementByAccessibilityId("LastGesture");
new Actions(_session) new Actions(Session)
.MoveToElement(border) .MoveToElement(border)
.DoubleClick() .DoubleClick()
.Perform(); .Perform();
Thread.Sleep(100); Thread.Sleep(100);
new Actions(_session).MoveToElement(lastGesture, 200, 200).DoubleClick().Perform(); new Actions(Session).MoveToElement(lastGesture, 200, 200).DoubleClick().Perform();
Assert.Equal("DoubleTapped2", lastGesture.Text); Assert.Equal("DoubleTapped2", lastGesture.Text);
} }
@ -135,10 +128,10 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void RightTapped_Is_Raised() public void RightTapped_Is_Raised()
{ {
var border = _session.FindElementByAccessibilityId("GestureBorder"); var border = Session.FindElementByAccessibilityId("GestureBorder");
var lastGesture = _session.FindElementByAccessibilityId("LastGesture"); var lastGesture = Session.FindElementByAccessibilityId("LastGesture");
new Actions(_session).ContextClick(border).Perform(); new Actions(Session).ContextClick(border).Perform();
Assert.Equal("RightTapped", lastGesture.Text); Assert.Equal("RightTapped", lastGesture.Text);
} }
@ -146,8 +139,8 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void RightTapped_Is_Raised_2() public void RightTapped_Is_Raised_2()
{ {
var border = _session.FindElementByAccessibilityId("GestureBorder"); var border = Session.FindElementByAccessibilityId("GestureBorder");
var lastGesture = _session.FindElementByAccessibilityId("LastGesture"); var lastGesture = Session.FindElementByAccessibilityId("LastGesture");
var device = new PointerInputDevice(PointerKind.Mouse); var device = new PointerInputDevice(PointerKind.Mouse);
var b = new ActionBuilder(); var b = new ActionBuilder();
@ -155,7 +148,7 @@ namespace Avalonia.IntegrationTests.Appium
b.AddAction(device.CreatePointerDown(MouseButton.Right)); b.AddAction(device.CreatePointerDown(MouseButton.Right));
b.AddAction(device.CreatePointerMove(border, 52, 52, TimeSpan.FromMilliseconds(50))); b.AddAction(device.CreatePointerMove(border, 52, 52, TimeSpan.FromMilliseconds(50)));
b.AddAction(device.CreatePointerUp(MouseButton.Right)); b.AddAction(device.CreatePointerUp(MouseButton.Right));
_session.PerformActions(b.ToActionSequenceList()); Session.PerformActions(b.ToActionSequenceList());
Assert.Equal("RightTapped", lastGesture.Text); Assert.Equal("RightTapped", lastGesture.Text);
} }
@ -163,8 +156,8 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void RightTapped_Is_Not_Raised_For_Drag() public void RightTapped_Is_Not_Raised_For_Drag()
{ {
var border = _session.FindElementByAccessibilityId("GestureBorder"); var border = Session.FindElementByAccessibilityId("GestureBorder");
var lastGesture = _session.FindElementByAccessibilityId("LastGesture"); var lastGesture = Session.FindElementByAccessibilityId("LastGesture");
var device = new PointerInputDevice(PointerKind.Mouse); var device = new PointerInputDevice(PointerKind.Mouse);
var b = new ActionBuilder(); var b = new ActionBuilder();

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

@ -7,17 +7,11 @@ using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public class ListBoxTests public class ListBoxTests : TestBase
{ {
private readonly AppiumDriver _session;
public ListBoxTests(DefaultAppFixture fixture) public ListBoxTests(DefaultAppFixture fixture)
: base(fixture, "ListBox")
{ {
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("ListBox");
tab.Click();
} }
[Fact] [Fact]
@ -49,7 +43,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.False(item2.Selected); Assert.False(item2.Selected);
Assert.False(item4.Selected); Assert.False(item4.Selected);
new Actions(_session) new Actions(Session)
.Click(item2) .Click(item2)
.KeyDown(Keys.Control) .KeyDown(Keys.Control)
.Click(item4) .Click(item4)
@ -73,7 +67,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.False(item3.Selected); Assert.False(item3.Selected);
Assert.False(item4.Selected); Assert.False(item4.Selected);
new Actions(_session) new Actions(Session)
.Click(item2) .Click(item2)
.KeyDown(Keys.Shift) .KeyDown(Keys.Shift)
.Click(item4) .Click(item4)
@ -96,8 +90,8 @@ namespace Avalonia.IntegrationTests.Appium
private AppiumWebElement GetTarget() private AppiumWebElement GetTarget()
{ {
_session.FindElementByAccessibilityId("ListBoxSelectionClear").Click(); Session.FindElementByAccessibilityId("ListBoxSelectionClear").Click();
return _session.FindElementByAccessibilityId("BasicListBox"); return Session.FindElementByAccessibilityId("BasicListBox");
} }
} }
} }

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

@ -1,59 +1,50 @@
using System.Threading; using OpenQA.Selenium;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Interactions; using OpenQA.Selenium.Interactions;
using Xunit; using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public abstract class MenuTests public abstract class MenuTests : TestBase
{ {
private readonly AppiumDriver _session;
public MenuTests(DefaultAppFixture fixture) public MenuTests(DefaultAppFixture fixture)
: base(fixture, "Menu")
{ {
_session = fixture.Session; var reset = Session.FindElementByAccessibilityId("MenuClickedMenuItemReset");
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("Menu");
tab.Click();
var reset = _session.FindElementByAccessibilityId("MenuClickedMenuItemReset");
reset.Click(); reset.Click();
var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem"); var clickedMenuItem = Session.FindElementByAccessibilityId("ClickedMenuItem");
Assert.Equal("None", clickedMenuItem.Text); Assert.Equal("None", clickedMenuItem.Text);
} }
[Fact] [Fact]
public void Click_Child() public void Click_Child()
{ {
var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem"); var rootMenuItem = Session.FindElementByAccessibilityId("RootMenuItem");
rootMenuItem.SendClick(); rootMenuItem.SendClick();
var childMenuItem = _session.FindElementByAccessibilityId("Child1MenuItem"); var childMenuItem = Session.FindElementByAccessibilityId("Child1MenuItem");
childMenuItem.SendClick(); childMenuItem.SendClick();
var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem"); var clickedMenuItem = Session.FindElementByAccessibilityId("ClickedMenuItem");
Assert.Equal("_Child 1", clickedMenuItem.Text); Assert.Equal("_Child 1", clickedMenuItem.Text);
} }
[Fact] [Fact]
public void Click_Grandchild() public void Click_Grandchild()
{ {
var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem"); var rootMenuItem = Session.FindElementByAccessibilityId("RootMenuItem");
rootMenuItem.SendClick(); rootMenuItem.SendClick();
var childMenuItem = _session.FindElementByAccessibilityId("Child2MenuItem"); var childMenuItem = Session.FindElementByAccessibilityId("Child2MenuItem");
childMenuItem.SendClick(); childMenuItem.SendClick();
var grandchildMenuItem = _session.FindElementByAccessibilityId("GrandchildMenuItem"); var grandchildMenuItem = Session.FindElementByAccessibilityId("GrandchildMenuItem");
grandchildMenuItem.SendClick(); grandchildMenuItem.SendClick();
var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem"); var clickedMenuItem = Session.FindElementByAccessibilityId("ClickedMenuItem");
Assert.Equal("_Grandchild", clickedMenuItem.Text); Assert.Equal("_Grandchild", clickedMenuItem.Text);
} }
@ -62,12 +53,12 @@ namespace Avalonia.IntegrationTests.Appium
{ {
MovePointerOutOfTheWay(); MovePointerOutOfTheWay();
new Actions(_session) new Actions(Session)
.KeyDown(Keys.Alt).KeyUp(Keys.Alt) .KeyDown(Keys.Alt).KeyUp(Keys.Alt)
.SendKeys(Keys.Down + Keys.Enter) .SendKeys(Keys.Down + Keys.Enter)
.Perform(); .Perform();
var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem"); var clickedMenuItem = Session.FindElementByAccessibilityId("ClickedMenuItem");
Assert.Equal("_Child 1", clickedMenuItem.Text); Assert.Equal("_Child 1", clickedMenuItem.Text);
} }
@ -76,12 +67,12 @@ namespace Avalonia.IntegrationTests.Appium
{ {
MovePointerOutOfTheWay(); MovePointerOutOfTheWay();
new Actions(_session) new Actions(Session)
.KeyDown(Keys.Alt).KeyUp(Keys.Alt) .KeyDown(Keys.Alt).KeyUp(Keys.Alt)
.SendKeys(Keys.Down + Keys.Down + Keys.Right + Keys.Enter) .SendKeys(Keys.Down + Keys.Down + Keys.Right + Keys.Enter)
.Perform(); .Perform();
var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem"); var clickedMenuItem = Session.FindElementByAccessibilityId("ClickedMenuItem");
Assert.Equal("_Grandchild", clickedMenuItem.Text); Assert.Equal("_Grandchild", clickedMenuItem.Text);
} }
@ -90,12 +81,12 @@ namespace Avalonia.IntegrationTests.Appium
{ {
MovePointerOutOfTheWay(); MovePointerOutOfTheWay();
new Actions(_session) new Actions(Session)
.KeyDown(Keys.Alt).KeyUp(Keys.Alt) .KeyDown(Keys.Alt).KeyUp(Keys.Alt)
.SendKeys("rc") .SendKeys("rc")
.Perform(); .Perform();
var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem"); var clickedMenuItem = Session.FindElementByAccessibilityId("ClickedMenuItem");
Assert.Equal("_Child 1", clickedMenuItem.Text); Assert.Equal("_Child 1", clickedMenuItem.Text);
} }
@ -104,55 +95,55 @@ namespace Avalonia.IntegrationTests.Appium
{ {
MovePointerOutOfTheWay(); MovePointerOutOfTheWay();
new Actions(_session) new Actions(Session)
.KeyDown(Keys.Alt).KeyUp(Keys.Alt) .KeyDown(Keys.Alt).KeyUp(Keys.Alt)
.SendKeys("rhg") .SendKeys("rhg")
.Perform(); .Perform();
var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem"); var clickedMenuItem = Session.FindElementByAccessibilityId("ClickedMenuItem");
Assert.Equal("_Grandchild", clickedMenuItem.Text); Assert.Equal("_Grandchild", clickedMenuItem.Text);
} }
[PlatformFact(TestPlatforms.Windows)] [PlatformFact(TestPlatforms.Windows)]
public void Select_Child_With_Click_Arrow_Keys() public void Select_Child_With_Click_Arrow_Keys()
{ {
var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem"); var rootMenuItem = Session.FindElementByAccessibilityId("RootMenuItem");
rootMenuItem.SendClick(); rootMenuItem.SendClick();
MovePointerOutOfTheWay(); MovePointerOutOfTheWay();
new Actions(_session) new Actions(Session)
.SendKeys(Keys.Down + Keys.Enter) .SendKeys(Keys.Down + Keys.Enter)
.Perform(); .Perform();
var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem"); var clickedMenuItem = Session.FindElementByAccessibilityId("ClickedMenuItem");
Assert.Equal("_Child 1", clickedMenuItem.Text); Assert.Equal("_Child 1", clickedMenuItem.Text);
} }
[PlatformFact(TestPlatforms.Windows)] [PlatformFact(TestPlatforms.Windows)]
public void Select_Grandchild_With_Click_Arrow_Keys() public void Select_Grandchild_With_Click_Arrow_Keys()
{ {
var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem"); var rootMenuItem = Session.FindElementByAccessibilityId("RootMenuItem");
rootMenuItem.SendClick(); rootMenuItem.SendClick();
MovePointerOutOfTheWay(); MovePointerOutOfTheWay();
new Actions(_session) new Actions(Session)
.SendKeys(Keys.Down + Keys.Down + Keys.Right + Keys.Enter) .SendKeys(Keys.Down + Keys.Down + Keys.Right + Keys.Enter)
.Perform(); .Perform();
var clickedMenuItem = _session.FindElementByAccessibilityId("ClickedMenuItem"); var clickedMenuItem = Session.FindElementByAccessibilityId("ClickedMenuItem");
Assert.Equal("_Grandchild", clickedMenuItem.Text); Assert.Equal("_Grandchild", clickedMenuItem.Text);
} }
[PlatformFact(TestPlatforms.Windows)] [PlatformFact(TestPlatforms.Windows)]
public void Child_AcceleratorKey() public void Child_AcceleratorKey()
{ {
var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem"); var rootMenuItem = Session.FindElementByAccessibilityId("RootMenuItem");
rootMenuItem.SendClick(); rootMenuItem.SendClick();
var childMenuItem = _session.FindElementByAccessibilityId("Child1MenuItem"); var childMenuItem = Session.FindElementByAccessibilityId("Child1MenuItem");
Assert.Equal("Ctrl+O", childMenuItem.GetAttribute("AcceleratorKey")); Assert.Equal("Ctrl+O", childMenuItem.GetAttribute("AcceleratorKey"));
} }
@ -161,12 +152,12 @@ namespace Avalonia.IntegrationTests.Appium
public void PointerOver_Does_Not_Steal_Focus() public void PointerOver_Does_Not_Steal_Focus()
{ {
// Issue #7906 // Issue #7906
var textBox = _session.FindElementByAccessibilityId("MenuFocusTest"); var textBox = Session.FindElementByAccessibilityId("MenuFocusTest");
textBox.Click(); textBox.Click();
Assert.True(textBox.GetIsFocused()); Assert.True(textBox.GetIsFocused());
var rootMenuItem = _session.FindElementByAccessibilityId("RootMenuItem"); var rootMenuItem = Session.FindElementByAccessibilityId("RootMenuItem");
rootMenuItem.MovePointerOver(); rootMenuItem.MovePointerOver();
Assert.True(textBox.GetIsFocused()); Assert.True(textBox.GetIsFocused());
@ -175,9 +166,9 @@ namespace Avalonia.IntegrationTests.Appium
private void MovePointerOutOfTheWay() private void MovePointerOutOfTheWay()
{ {
// Move the pointer to the menu tab item so that it's not over the menu in preparation // Move the pointer to the menu tab item so that it's not over the menu in preparation
// for key press tests. This prevents the mouse accidentially selecting the wrong item // for key press tests. This prevents the mouse accidentally selecting the wrong item
// by hovering. // by hovering.
var tabs = _session.FindElementByAccessibilityId("MainTabs"); var tabs = Session.FindElementByAccessibilityId("Pager");
var tab = tabs.FindElementByName("Menu"); var tab = tabs.FindElementByName("Menu");
tab.MovePointerOver(); tab.MovePointerOver();
} }

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

@ -1,29 +1,22 @@
using System.Threading; using System.Threading;
using OpenQA.Selenium.Appium;
using Xunit; using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public class NativeMenuTests public class NativeMenuTests : TestBase
{ {
private readonly AppiumDriver _session;
public NativeMenuTests(DefaultAppFixture fixture) public NativeMenuTests(DefaultAppFixture fixture)
: base(fixture, "Automation")
{ {
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("Automation");
tab.Click();
} }
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void MacOS_View_Menu_Select_Button_Tab() public void MacOS_View_Menu_Select_Button_Tab()
{ {
var tabs = _session.FindElementByAccessibilityId("MainTabs"); var tabs = Session.FindElementByAccessibilityId("Pager");
var buttonTab = tabs.FindElementByName("Button"); var buttonTab = tabs.FindElementByName("Button");
var menuBar = _session.FindElementByXPath("/XCUIElementTypeApplication/XCUIElementTypeMenuBar"); var menuBar = Session.FindElementByXPath("/XCUIElementTypeApplication/XCUIElementTypeMenuBar");
var viewMenu = menuBar.FindElementByName("View"); var viewMenu = menuBar.FindElementByName("View");
Assert.False(buttonTab.Selected); Assert.False(buttonTab.Selected);
@ -38,9 +31,9 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.Windows)] [PlatformFact(TestPlatforms.Windows)]
public void Win32_View_Menu_Select_Button_Tab() public void Win32_View_Menu_Select_Button_Tab()
{ {
var tabs = _session.FindElementByAccessibilityId("MainTabs"); var tabs = Session.FindElementByAccessibilityId("Pager");
var buttonTab = tabs.FindElementByName("Button"); var buttonTab = tabs.FindElementByName("Button");
var viewMenu = _session.FindElementByXPath("//MenuItem[@Name='View']"); var viewMenu = Session.FindElementByXPath("//MenuItem[@Name='View']");
Assert.False(buttonTab.Selected); Assert.False(buttonTab.Selected);
@ -54,7 +47,7 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void MacOS_Sanitizes_Access_Key_Markers_When_Included_In_Menu_Title() public void MacOS_Sanitizes_Access_Key_Markers_When_Included_In_Menu_Title()
{ {
var menuBar = _session.FindElementByXPath("/XCUIElementTypeApplication/XCUIElementTypeMenuBar"); var menuBar = Session.FindElementByXPath("/XCUIElementTypeApplication/XCUIElementTypeMenuBar");
Assert.True(menuBar.FindElementsByName("_Options").Count == 0); Assert.True(menuBar.FindElementsByName("_Options").Count == 0);
Assert.True(menuBar.FindElementsByName("Options").Count == 1); Assert.True(menuBar.FindElementsByName("Options").Count == 1);
@ -63,7 +56,7 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.Windows)] [PlatformFact(TestPlatforms.Windows)]
public void Win32_Avalonia_Menu_Has_ToolTip_If_Defined() public void Win32_Avalonia_Menu_Has_ToolTip_If_Defined()
{ {
var viewMenu = _session.FindElementByXPath("//MenuItem[@Name='View']"); var viewMenu = Session.FindElementByXPath("//MenuItem[@Name='View']");
viewMenu.Click(); viewMenu.Click();
var buttonMenuItem = viewMenu.FindElementByName("Button"); var buttonMenuItem = viewMenu.FindElementByName("Button");
@ -72,14 +65,14 @@ namespace Avalonia.IntegrationTests.Appium
// Wait for tooltip to open. // Wait for tooltip to open.
Thread.Sleep(2000); Thread.Sleep(2000);
var toolTipCandidates = _session.FindElementsByClassName("TextBlock"); var toolTipCandidates = Session.FindElementsByClassName("TextBlock");
Assert.Contains(toolTipCandidates, x => x.Text == "Tip:Button"); Assert.Contains(toolTipCandidates, x => x.Text == "Tip:Button");
} }
[PlatformFact(TestPlatforms.MacOS, Skip = "Flaky test")] [PlatformFact(TestPlatforms.MacOS, Skip = "Flaky test")]
public void MacOS_Native_Menu_Has_ToolTip_If_Defined() public void MacOS_Native_Menu_Has_ToolTip_If_Defined()
{ {
var menuBar = _session.FindElementByXPath("/XCUIElementTypeApplication/XCUIElementTypeMenuBar"); var menuBar = Session.FindElementByXPath("/XCUIElementTypeApplication/XCUIElementTypeMenuBar");
var viewMenu = menuBar.FindElementByName("View"); var viewMenu = menuBar.FindElementByName("View");
viewMenu.Click(); viewMenu.Click();
@ -89,7 +82,7 @@ namespace Avalonia.IntegrationTests.Appium
// Wait for tooltip to open. // Wait for tooltip to open.
Thread.Sleep(4000); Thread.Sleep(4000);
var toolTipCandidates = _session.FindElementsByClassName("XCUIElementTypeStaticText"); var toolTipCandidates = Session.FindElementsByClassName("XCUIElementTypeStaticText");
Assert.Contains(toolTipCandidates, x => x.Text == "Tip:Button"); Assert.Contains(toolTipCandidates, x => x.Text == "Tip:Button");
} }
} }

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

@ -4,23 +4,17 @@ using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public class RadioButtonTests public class RadioButtonTests : TestBase
{ {
private readonly AppiumDriver _session;
public RadioButtonTests(DefaultAppFixture fixture) public RadioButtonTests(DefaultAppFixture fixture)
: base(fixture, "RadioButton")
{ {
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
tabs.FindElementByName("RadioButton").Click();
} }
[Fact] [Fact]
public void RadioButton_IsChecked_True_When_Clicked() public void RadioButton_IsChecked_True_When_Clicked()
{ {
var button = _session.FindElementByAccessibilityId("BasicRadioButton"); var button = Session.FindElementByAccessibilityId("BasicRadioButton");
Assert.False(button.GetIsChecked()); Assert.False(button.GetIsChecked());
button.Click(); button.Click();
Assert.True(button.GetIsChecked()); Assert.True(button.GetIsChecked());
@ -29,8 +23,8 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void ThreeState_RadioButton_IsChecked_False_When_Other_ThreeState_RadioButton_Checked() public void ThreeState_RadioButton_IsChecked_False_When_Other_ThreeState_RadioButton_Checked()
{ {
var button1 = _session.FindElementByAccessibilityId("ThreeStatesRadioButton1"); var button1 = Session.FindElementByAccessibilityId("ThreeStatesRadioButton1");
var button2 = _session.FindElementByAccessibilityId("ThreeStatesRadioButton2"); var button2 = Session.FindElementByAccessibilityId("ThreeStatesRadioButton2");
Assert.True(button1.GetIsChecked()); Assert.True(button1.GetIsChecked());
Assert.False(button2.GetIsChecked()); Assert.False(button2.GetIsChecked());
button2.Click(); button2.Click();

12
tests/Avalonia.IntegrationTests.Appium/ScrollBarTests.cs

@ -4,23 +4,17 @@ using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public class ScrollBarTests public class ScrollBarTests : TestBase
{ {
private readonly AppiumDriver _session;
public ScrollBarTests(DefaultAppFixture fixture) public ScrollBarTests(DefaultAppFixture fixture)
: base(fixture, "ScrollBar")
{ {
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("ScrollBar");
tab.Click();
} }
[Fact] [Fact]
public void ScrollBar_Increases_Value_By_LargeChange_When_IncreaseButton_Is_Clicked() public void ScrollBar_Increases_Value_By_LargeChange_When_IncreaseButton_Is_Clicked()
{ {
var button = _session.FindElementByAccessibilityId("MyScrollBar"); var button = Session.FindElementByAccessibilityId("MyScrollBar");
Assert.True(double.Parse(button.Text) == 20); Assert.True(double.Parse(button.Text) == 20);
button.Click(); button.Click();

37
tests/Avalonia.IntegrationTests.Appium/SliderTests.cs

@ -7,34 +7,27 @@ using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public class SliderTests public class SliderTests : TestBase
{ {
private readonly AppiumDriver _session;
public SliderTests(DefaultAppFixture fixture) public SliderTests(DefaultAppFixture fixture)
: base(fixture, "Slider")
{ {
_session = fixture.Session; var reset = Session.FindElementByAccessibilityId("ResetSliders");
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("Slider");
tab.Click();
var reset = _session.FindElementByAccessibilityId("ResetSliders");
reset.Click(); reset.Click();
} }
[Fact] [Fact]
public void Horizontal_Changes_Value_Dragging_Thumb_Right() public void Horizontal_Changes_Value_Dragging_Thumb_Right()
{ {
var slider = _session.FindElementByAccessibilityId("HorizontalSlider"); var slider = Session.FindElementByAccessibilityId("HorizontalSlider");
var thumb = slider.FindElementByAccessibilityId("thumb"); var thumb = slider.FindElementByAccessibilityId("thumb");
var initialThumbRect = thumb.Rect; var initialThumbRect = thumb.Rect;
new Actions(_session).ClickAndHold(thumb).MoveByOffset(100, 0).Release().Perform(); new Actions(Session).ClickAndHold(thumb).MoveByOffset(100, 0).Release().Perform();
var value = Math.Round(double.Parse(slider.Text, CultureInfo.InvariantCulture)); var value = Math.Round(double.Parse(slider.Text, CultureInfo.InvariantCulture));
var boundValue = double.Parse( var boundValue = double.Parse(
_session.FindElementByAccessibilityId("HorizontalSliderValue").Text, Session.FindElementByAccessibilityId("HorizontalSliderValue").Text,
CultureInfo.InvariantCulture); CultureInfo.InvariantCulture);
Assert.True(value > 50); Assert.True(value > 50);
@ -47,15 +40,15 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void Horizontal_Changes_Value_Dragging_Thumb_Left() public void Horizontal_Changes_Value_Dragging_Thumb_Left()
{ {
var slider = _session.FindElementByAccessibilityId("HorizontalSlider"); var slider = Session.FindElementByAccessibilityId("HorizontalSlider");
var thumb = slider.FindElementByAccessibilityId("thumb"); var thumb = slider.FindElementByAccessibilityId("thumb");
var initialThumbRect = thumb.Rect; var initialThumbRect = thumb.Rect;
new Actions(_session).ClickAndHold(thumb).MoveByOffset(-100, 0).Release().Perform(); new Actions(Session).ClickAndHold(thumb).MoveByOffset(-100, 0).Release().Perform();
var value = Math.Round(double.Parse(slider.Text, CultureInfo.InvariantCulture)); var value = Math.Round(double.Parse(slider.Text, CultureInfo.InvariantCulture));
var boundValue = double.Parse( var boundValue = double.Parse(
_session.FindElementByAccessibilityId("HorizontalSliderValue").Text, Session.FindElementByAccessibilityId("HorizontalSliderValue").Text,
CultureInfo.InvariantCulture); CultureInfo.InvariantCulture);
Assert.True(value < 50); Assert.True(value < 50);
@ -68,15 +61,15 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void Horizontal_Changes_Value_When_Clicking_Increase_Button() public void Horizontal_Changes_Value_When_Clicking_Increase_Button()
{ {
var slider = _session.FindElementByAccessibilityId("HorizontalSlider"); var slider = Session.FindElementByAccessibilityId("HorizontalSlider");
var thumb = slider.FindElementByAccessibilityId("thumb"); var thumb = slider.FindElementByAccessibilityId("thumb");
var initialThumbRect = thumb.Rect; var initialThumbRect = thumb.Rect;
new Actions(_session).MoveToElementCenter(slider, 100, 0).Click().Perform(); new Actions(Session).MoveToElementCenter(slider, 100, 0).Click().Perform();
var value = Math.Round(double.Parse(slider.Text, CultureInfo.InvariantCulture)); var value = Math.Round(double.Parse(slider.Text, CultureInfo.InvariantCulture));
var boundValue = double.Parse( var boundValue = double.Parse(
_session.FindElementByAccessibilityId("HorizontalSliderValue").Text, Session.FindElementByAccessibilityId("HorizontalSliderValue").Text,
CultureInfo.InvariantCulture); CultureInfo.InvariantCulture);
Assert.True(value > 50); Assert.True(value > 50);
@ -89,15 +82,15 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void Horizontal_Changes_Value_When_Clicking_Decrease_Button() public void Horizontal_Changes_Value_When_Clicking_Decrease_Button()
{ {
var slider = _session.FindElementByAccessibilityId("HorizontalSlider"); var slider = Session.FindElementByAccessibilityId("HorizontalSlider");
var thumb = slider.FindElementByAccessibilityId("thumb"); var thumb = slider.FindElementByAccessibilityId("thumb");
var initialThumbRect = thumb.Rect; var initialThumbRect = thumb.Rect;
new Actions(_session).MoveToElementCenter(slider, -100, 0).Click().Perform(); new Actions(Session).MoveToElementCenter(slider, -100, 0).Click().Perform();
var value = Math.Round(double.Parse(slider.Text, CultureInfo.InvariantCulture)); var value = Math.Round(double.Parse(slider.Text, CultureInfo.InvariantCulture));
var boundValue = double.Parse( var boundValue = double.Parse(
_session.FindElementByAccessibilityId("HorizontalSliderValue").Text, Session.FindElementByAccessibilityId("HorizontalSliderValue").Text,
CultureInfo.InvariantCulture); CultureInfo.InvariantCulture);
Assert.True(value < 50); Assert.True(value < 50);

33
tests/Avalonia.IntegrationTests.Appium/TestBase.cs

@ -0,0 +1,33 @@
using OpenQA.Selenium;
using System.Threading;
namespace Avalonia.IntegrationTests.Appium;
public class TestBase
{
protected TestBase(DefaultAppFixture fixture, string pageName)
{
Session = fixture.Session;
var retry = 0;
for (;;)
{
try
{
var pager = Session.FindElementByAccessibilityId("Pager");
var page = pager.FindElementByName(pageName);
page.Click();
break;
}
catch (WebDriverException) when (retry++ < 3)
{
// MacOS sometimes seems to need a bit of time to get itself back in order after switching out
// of fullscreen.
Thread.Sleep(1000);
}
}
}
protected AppiumDriver Session { get; }
}

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

@ -13,17 +13,11 @@ using Xunit.Sdk;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public class WindowTests public class WindowTests : TestBase
{ {
private readonly AppiumDriver _session;
public WindowTests(DefaultAppFixture fixture) public WindowTests(DefaultAppFixture fixture)
: base(fixture, "Window")
{ {
_session = fixture.Session;
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("Window");
tab.Click();
} }
[Theory] [Theory]
@ -90,8 +84,8 @@ namespace Avalonia.IntegrationTests.Appium
{ {
try try
{ {
_session.FindElementByAccessibilityId("CurrentWindowState").SendClick(); Session.FindElementByAccessibilityId("CurrentWindowState").SendClick();
_session.FindElementByAccessibilityId("WindowStateNormal").SendClick(); Session.FindElementByAccessibilityId("WindowStateNormal").SendClick();
// Wait for animations to run. // Wait for animations to run.
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
@ -110,14 +104,14 @@ namespace Avalonia.IntegrationTests.Appium
{ {
using (OpenWindow(new Size(400, 400), ShowWindowMode.NonOwned, WindowStartupLocation.Manual)) using (OpenWindow(new Size(400, 400), ShowWindowMode.NonOwned, WindowStartupLocation.Manual))
{ {
var windowState = _session.FindElementByAccessibilityId("CurrentWindowState"); var windowState = Session.FindElementByAccessibilityId("CurrentWindowState");
Assert.Equal("Normal", windowState.GetComboBoxValue()); Assert.Equal("Normal", windowState.GetComboBoxValue());
var window = _session.FindElements(By.XPath("//Window")).First(); var window = Session.FindElements(By.XPath("//Window")).First();
new Actions(_session) new Actions(Session)
.KeyDown(Keys.Meta) .KeyDown(Keys.Meta)
.SendKeys(Keys.Left) .SendKeys(Keys.Left)
.KeyUp(Keys.Meta) .KeyUp(Keys.Meta)
@ -126,9 +120,9 @@ namespace Avalonia.IntegrationTests.Appium
var original = GetWindowInfo(); var original = GetWindowInfo();
windowState.Click(); windowState.Click();
_session.FindElementByName("Minimized").SendClick(); Session.FindElementByName("Minimized").SendClick();
new Actions(_session) new Actions(Session)
.KeyDown(Keys.Alt) .KeyDown(Keys.Alt)
.SendKeys(Keys.Tab) .SendKeys(Keys.Tab)
.KeyUp(Keys.Alt) .KeyUp(Keys.Alt)
@ -147,8 +141,8 @@ namespace Avalonia.IntegrationTests.Appium
{ {
using (OpenWindow(new Size(4000, 2200), ShowWindowMode.NonOwned, WindowStartupLocation.Manual)) using (OpenWindow(new Size(4000, 2200), ShowWindowMode.NonOwned, WindowStartupLocation.Manual))
{ {
var screenRectTextBox = _session.FindElementByAccessibilityId("CurrentClientSize"); var screenRectTextBox = Session.FindElementByAccessibilityId("CurrentClientSize");
var measuredWithTextBlock = _session.FindElementByAccessibilityId("CurrentMeasuredWithText"); var measuredWithTextBlock = Session.FindElementByAccessibilityId("CurrentMeasuredWithText");
var measuredWithString = measuredWithTextBlock.Text; var measuredWithString = measuredWithTextBlock.Text;
var workingAreaString = screenRectTextBox.Text; var workingAreaString = screenRectTextBox.Text;
@ -167,17 +161,17 @@ namespace Avalonia.IntegrationTests.Appium
public void ShowMode(ShowWindowMode mode) public void ShowMode(ShowWindowMode mode)
{ {
using var window = OpenWindow(null, mode, WindowStartupLocation.Manual); using var window = OpenWindow(null, mode, WindowStartupLocation.Manual);
var windowState = _session.FindElementByAccessibilityId("CurrentWindowState"); var windowState = Session.FindElementByAccessibilityId("CurrentWindowState");
var original = GetWindowInfo(); var original = GetWindowInfo();
Assert.Equal("Normal", windowState.GetComboBoxValue()); Assert.Equal("Normal", windowState.GetComboBoxValue());
windowState.Click(); windowState.Click();
_session.FindElementByAccessibilityId("WindowStateMaximized").SendClick(); Session.FindElementByAccessibilityId("WindowStateMaximized").SendClick();
Assert.Equal("Maximized", windowState.GetComboBoxValue()); Assert.Equal("Maximized", windowState.GetComboBoxValue());
windowState.Click(); windowState.Click();
_session.FindElementByAccessibilityId("WindowStateNormal").SendClick(); Session.FindElementByAccessibilityId("WindowStateNormal").SendClick();
var current = GetWindowInfo(); var current = GetWindowInfo();
Assert.Equal(original.Position, current.Position); Assert.Equal(original.Position, current.Position);
@ -187,7 +181,7 @@ namespace Avalonia.IntegrationTests.Appium
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || mode == ShowWindowMode.NonOwned) if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || mode == ShowWindowMode.NonOwned)
{ {
windowState.Click(); windowState.Click();
_session.FindElementByAccessibilityId("WindowStateFullScreen").SendClick(); Session.FindElementByAccessibilityId("WindowStateFullScreen").SendClick();
Assert.Equal("FullScreen", windowState.GetComboBoxValue()); Assert.Equal("FullScreen", windowState.GetComboBoxValue());
current = GetWindowInfo(); current = GetWindowInfo();
@ -197,7 +191,7 @@ namespace Avalonia.IntegrationTests.Appium
windowState.SendClick(); windowState.SendClick();
_session.FindElementByAccessibilityId("WindowStateNormal").SendClick(); Session.FindElementByAccessibilityId("WindowStateNormal").SendClick();
current = GetWindowInfo(); current = GetWindowInfo();
Assert.Equal(original.Position, current.Position); Assert.Equal(original.Position, current.Position);
@ -210,7 +204,7 @@ namespace Avalonia.IntegrationTests.Appium
{ {
var clientSize = new Size(400, 400); var clientSize = new Size(400, 400);
using var window = OpenWindow(clientSize, ShowWindowMode.NonOwned, WindowStartupLocation.CenterScreen, extendClientArea: true); using var window = OpenWindow(clientSize, ShowWindowMode.NonOwned, WindowStartupLocation.CenterScreen, extendClientArea: true);
var windowState = _session.FindElementByAccessibilityId("CurrentWindowState"); var windowState = Session.FindElementByAccessibilityId("CurrentWindowState");
var current = GetWindowInfo(); var current = GetWindowInfo();
Assert.Equal(current.ClientSize, clientSize); Assert.Equal(current.ClientSize, clientSize);
@ -219,11 +213,11 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void TransparentWindow() public void TransparentWindow()
{ {
var showTransparentWindow = _session.FindElementByAccessibilityId("ShowTransparentWindow"); var showTransparentWindow = Session.FindElementByAccessibilityId("ShowTransparentWindow");
showTransparentWindow.Click(); showTransparentWindow.Click();
Thread.Sleep(1000); Thread.Sleep(1000);
var window = _session.FindElementByAccessibilityId("TransparentWindow"); var window = Session.FindElementByAccessibilityId("TransparentWindow");
var screenshot = window.GetScreenshot(); var screenshot = window.GetScreenshot();
window.Click(); window.Click();
@ -239,11 +233,11 @@ namespace Avalonia.IntegrationTests.Appium
[Fact] [Fact]
public void TransparentPopup() public void TransparentPopup()
{ {
var showTransparentWindow = _session.FindElementByAccessibilityId("ShowTransparentPopup"); var showTransparentWindow = Session.FindElementByAccessibilityId("ShowTransparentPopup");
showTransparentWindow.Click(); showTransparentWindow.Click();
Thread.Sleep(1000); Thread.Sleep(1000);
var window = _session.FindElementByAccessibilityId("TransparentPopupBackground"); var window = Session.FindElementByAccessibilityId("TransparentPopupBackground");
var container = window.FindElementByAccessibilityId("PopupContainer"); var container = window.FindElementByAccessibilityId("PopupContainer");
var screenshot = container.GetScreenshot(); var screenshot = container.GetScreenshot();
@ -268,7 +262,7 @@ namespace Avalonia.IntegrationTests.Appium
{ {
using (OpenWindow(null, mode, WindowStartupLocation.Manual, canResize: false, extendClientArea: extendClientArea)) using (OpenWindow(null, mode, WindowStartupLocation.Manual, canResize: false, extendClientArea: extendClientArea))
{ {
var secondaryWindow = GetWindow("SecondaryWindow"); var secondaryWindow = Session.GetWindowById("SecondaryWindow");
AppiumWebElement? maximizeButton; AppiumWebElement? maximizeButton;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@ -375,13 +369,13 @@ namespace Avalonia.IntegrationTests.Appium
bool canResize = true, bool canResize = true,
bool extendClientArea = false) bool extendClientArea = false)
{ {
var sizeTextBox = _session.FindElementByAccessibilityId("ShowWindowSize"); var sizeTextBox = Session.FindElementByAccessibilityId("ShowWindowSize");
var modeComboBox = _session.FindElementByAccessibilityId("ShowWindowMode"); var modeComboBox = Session.FindElementByAccessibilityId("ShowWindowMode");
var locationComboBox = _session.FindElementByAccessibilityId("ShowWindowLocation"); var locationComboBox = Session.FindElementByAccessibilityId("ShowWindowLocation");
var stateComboBox = _session.FindElementByAccessibilityId("ShowWindowState"); var stateComboBox = Session.FindElementByAccessibilityId("ShowWindowState");
var canResizeCheckBox = _session.FindElementByAccessibilityId("ShowWindowCanResize"); var canResizeCheckBox = Session.FindElementByAccessibilityId("ShowWindowCanResize");
var showButton = _session.FindElementByAccessibilityId("ShowWindow"); var showButton = Session.FindElementByAccessibilityId("ShowWindow");
var extendClientAreaCheckBox = _session.FindElementByAccessibilityId("ShowWindowExtendClientAreaToDecorationsHint"); var extendClientAreaCheckBox = Session.FindElementByAccessibilityId("ShowWindowExtendClientAreaToDecorationsHint");
if (size.HasValue) if (size.HasValue)
sizeTextBox.SendKeys($"{size.Value.Width}, {size.Value.Height}"); sizeTextBox.SendKeys($"{size.Value.Width}, {size.Value.Height}");
@ -389,19 +383,19 @@ namespace Avalonia.IntegrationTests.Appium
if (modeComboBox.GetComboBoxValue() != mode.ToString()) if (modeComboBox.GetComboBoxValue() != mode.ToString())
{ {
modeComboBox.Click(); modeComboBox.Click();
_session.FindElementByName(mode.ToString()).SendClick(); Session.FindElementByName(mode.ToString()).SendClick();
} }
if (locationComboBox.GetComboBoxValue() != location.ToString()) if (locationComboBox.GetComboBoxValue() != location.ToString())
{ {
locationComboBox.Click(); locationComboBox.Click();
_session.FindElementByName(location.ToString()).SendClick(); Session.FindElementByName(location.ToString()).SendClick();
} }
if (stateComboBox.GetComboBoxValue() != state.ToString()) if (stateComboBox.GetComboBoxValue() != state.ToString())
{ {
stateComboBox.Click(); stateComboBox.Click();
_session.FindElementByAccessibilityId($"ShowWindowState{state}").SendClick(); Session.FindElementByAccessibilityId($"ShowWindowState{state}").SendClick();
} }
if (canResizeCheckBox.GetIsChecked() != canResize) if (canResizeCheckBox.GetIsChecked() != canResize)
@ -432,7 +426,7 @@ namespace Avalonia.IntegrationTests.Appium
{ {
PixelRect? ReadOwnerRect() PixelRect? ReadOwnerRect()
{ {
var text = _session.FindElementByAccessibilityId("CurrentOwnerRect").Text; var text = Session.FindElementByAccessibilityId("CurrentOwnerRect").Text;
return !string.IsNullOrWhiteSpace(text) ? PixelRect.Parse(text) : null; return !string.IsNullOrWhiteSpace(text) ? PixelRect.Parse(text) : null;
} }
@ -443,13 +437,13 @@ namespace Avalonia.IntegrationTests.Appium
try try
{ {
return new( return new(
Size.Parse(_session.FindElementByAccessibilityId("CurrentClientSize").Text), Size.Parse(Session.FindElementByAccessibilityId("CurrentClientSize").Text),
Size.Parse(_session.FindElementByAccessibilityId("CurrentFrameSize").Text), Size.Parse(Session.FindElementByAccessibilityId("CurrentFrameSize").Text),
PixelPoint.Parse(_session.FindElementByAccessibilityId("CurrentPosition").Text), PixelPoint.Parse(Session.FindElementByAccessibilityId("CurrentPosition").Text),
ReadOwnerRect(), ReadOwnerRect(),
PixelRect.Parse(_session.FindElementByAccessibilityId("CurrentScreenRect").Text), PixelRect.Parse(Session.FindElementByAccessibilityId("CurrentScreenRect").Text),
double.Parse(_session.FindElementByAccessibilityId("CurrentScaling").Text), double.Parse(Session.FindElementByAccessibilityId("CurrentScaling").Text),
Enum.Parse<WindowState>(_session.FindElementByAccessibilityId("CurrentWindowState").Text)); Enum.Parse<WindowState>(Session.FindElementByAccessibilityId("CurrentWindowState").Text));
} }
catch (OpenQA.Selenium.NoSuchElementException) when (retry++ < 3) catch (OpenQA.Selenium.NoSuchElementException) when (retry++ < 3)
{ {

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

@ -1,10 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Utilities;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium; using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Interactions; using OpenQA.Selenium.Interactions;
using Xunit; using Xunit;
@ -12,39 +9,17 @@ using Xunit;
namespace Avalonia.IntegrationTests.Appium namespace Avalonia.IntegrationTests.Appium
{ {
[Collection("Default")] [Collection("Default")]
public class WindowTests_MacOS public class WindowTests_MacOS : TestBase
{ {
private readonly AppiumDriver _session;
public WindowTests_MacOS(DefaultAppFixture fixture) public WindowTests_MacOS(DefaultAppFixture fixture)
: base(fixture, "Window")
{ {
var retry = 0;
_session = fixture.Session;
for (;;)
{
try
{
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var tab = tabs.FindElementByName("Window");
tab.Click();
return;
}
catch (WebDriverException) when (retry++ < 3)
{
// MacOS sometimes seems to need a bit of time to get itself back in order after switching out
// of fullscreen.
Thread.Sleep(1000);
}
}
} }
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void WindowOrder_Modal_Dialog_Stays_InFront_Of_Parent() public void WindowOrder_Modal_Dialog_Stays_InFront_Of_Parent()
{ {
var mainWindow = _session.FindElementByAccessibilityId("MainWindow"); var mainWindow = Session.FindElementByAccessibilityId("MainWindow");
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.Manual)) using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.Manual))
{ {
@ -63,14 +38,14 @@ namespace Avalonia.IntegrationTests.Appium
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.Manual)) using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Modal, WindowStartupLocation.Manual))
{ {
new Actions(_session) new Actions(Session)
.MoveToElement(mainWindow, 100, 1) .MoveToElement(mainWindow, 100, 1)
.ClickAndHold() .ClickAndHold()
.Perform(); .Perform();
var secondaryWindowIndex = GetWindowOrder("SecondaryWindow"); var secondaryWindowIndex = GetWindowOrder("SecondaryWindow");
new Actions(_session) new Actions(Session)
.MoveToElement(mainWindow, 100, 1) .MoveToElement(mainWindow, 100, 1)
.Release() .Release()
.Perform(); .Perform();
@ -99,14 +74,14 @@ namespace Avalonia.IntegrationTests.Appium
} }
finally finally
{ {
_session.FindElementByAccessibilityId("ExitFullscreen").Click(); Session.FindElementByAccessibilityId("ExitFullscreen").Click();
} }
} }
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void WindowOrder_Owned_Dialog_Stays_InFront_Of_Parent() public void WindowOrder_Owned_Dialog_Stays_InFront_Of_Parent()
{ {
var mainWindow = _session.FindElementByAccessibilityId("MainWindow"); var mainWindow = Session.FindElementByAccessibilityId("MainWindow");
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Owned, WindowStartupLocation.Manual)) using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Owned, WindowStartupLocation.Manual))
{ {
@ -119,7 +94,7 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void WindowOrder_Owned_Dialog_Stays_InFront_Of_FullScreen_Parent() public void WindowOrder_Owned_Dialog_Stays_InFront_Of_FullScreen_Parent()
{ {
var mainWindow = _session.FindElementByAccessibilityId("MainWindow"); var mainWindow = Session.FindElementByAccessibilityId("MainWindow");
// Enter fullscreen // Enter fullscreen
mainWindow.FindElementByAccessibilityId("EnterFullscreen").Click(); mainWindow.FindElementByAccessibilityId("EnterFullscreen").Click();
@ -146,7 +121,7 @@ namespace Avalonia.IntegrationTests.Appium
Thread.Sleep(1000); Thread.Sleep(1000);
// Make sure we exited fullscreen. // Make sure we exited fullscreen.
mainWindow = _session.FindElementByAccessibilityId("MainWindow"); mainWindow = Session.FindElementByAccessibilityId("MainWindow");
windowState = mainWindow.FindElementByAccessibilityId("MainWindowState"); windowState = mainWindow.FindElementByAccessibilityId("MainWindowState");
Assert.Equal("Normal", windowState.Text); Assert.Equal("Normal", windowState.Text);
} }
@ -167,7 +142,7 @@ namespace Avalonia.IntegrationTests.Appium
public void Does_Not_Switch_Space_From_FullScreen_To_Main_Desktop_When_FullScreen_Window_Clicked() public void Does_Not_Switch_Space_From_FullScreen_To_Main_Desktop_When_FullScreen_Window_Clicked()
{ {
// Issue #9565 // Issue #9565
var mainWindow = _session.FindElementByAccessibilityId("MainWindow"); var mainWindow = Session.FindElementByAccessibilityId("MainWindow");
AppiumWebElement windowState; AppiumWebElement windowState;
// Open child window. // Open child window.
@ -180,7 +155,7 @@ namespace Avalonia.IntegrationTests.Appium
Thread.Sleep(1000); Thread.Sleep(1000);
// Make sure we entered fullscreen. // Make sure we entered fullscreen.
mainWindow = _session.FindElementByAccessibilityId("MainWindow"); mainWindow = Session.FindElementByAccessibilityId("MainWindow");
windowState = mainWindow.FindElementByAccessibilityId("MainWindowState"); windowState = mainWindow.FindElementByAccessibilityId("MainWindowState");
Assert.Equal("FullScreen", windowState.Text); Assert.Equal("FullScreen", windowState.Text);
@ -196,7 +171,7 @@ namespace Avalonia.IntegrationTests.Appium
} }
// Make sure we exited fullscreen. // Make sure we exited fullscreen.
mainWindow = _session.FindElementByAccessibilityId("MainWindow"); mainWindow = Session.FindElementByAccessibilityId("MainWindow");
windowState = mainWindow.FindElementByAccessibilityId("MainWindowState"); windowState = mainWindow.FindElementByAccessibilityId("MainWindowState");
Assert.Equal("Normal", windowState.Text); Assert.Equal("Normal", windowState.Text);
} }
@ -204,7 +179,7 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void WindowOrder_NonOwned_Window_Does_Not_Stay_InFront_Of_Parent() public void WindowOrder_NonOwned_Window_Does_Not_Stay_InFront_Of_Parent()
{ {
var mainWindow = _session.FindElementByAccessibilityId("MainWindow"); var mainWindow = Session.FindElementByAccessibilityId("MainWindow");
using (OpenWindow(new PixelSize(800, 100), ShowWindowMode.NonOwned, WindowStartupLocation.Manual)) using (OpenWindow(new PixelSize(800, 100), ShowWindowMode.NonOwned, WindowStartupLocation.Manual))
{ {
@ -214,7 +189,7 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal(2, secondaryWindowIndex); Assert.Equal(2, secondaryWindowIndex);
var sendToBack = _session.FindElementByAccessibilityId("SendToBack"); var sendToBack = Session.FindElementByAccessibilityId("SendToBack");
sendToBack.Click(); sendToBack.Click();
} }
} }
@ -294,14 +269,14 @@ namespace Avalonia.IntegrationTests.Appium
miniaturizeButton.Click(); miniaturizeButton.Click();
Thread.Sleep(1000); Thread.Sleep(1000);
var hittable = _session.FindElementsByXPath("/XCUIElementTypeApplication/XCUIElementTypeWindow") var hittable = Session.FindElementsByXPath("/XCUIElementTypeApplication/XCUIElementTypeWindow")
.Select(x => x.GetAttribute("hittable")).ToList(); .Select(x => x.GetAttribute("hittable")).ToList();
Assert.Equal(new[] { "true", "false" }, hittable); Assert.Equal(new[] { "true", "false" }, hittable);
_session.FindElementByAccessibilityId("RestoreAll").Click(); Session.FindElementByAccessibilityId("RestoreAll").Click();
Thread.Sleep(1000); Thread.Sleep(1000);
hittable = _session.FindElementsByXPath("/XCUIElementTypeApplication/XCUIElementTypeWindow") hittable = Session.FindElementsByXPath("/XCUIElementTypeApplication/XCUIElementTypeWindow")
.Select(x => x.GetAttribute("hittable")).ToList(); .Select(x => x.GetAttribute("hittable")).ToList();
Assert.Equal(new[] { "true", "true" }, hittable); Assert.Equal(new[] { "true", "true" }, hittable);
} }
@ -310,7 +285,7 @@ namespace Avalonia.IntegrationTests.Appium
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void Hidden_Child_Window_Is_Not_Reshown_When_Parent_Clicked() public void Hidden_Child_Window_Is_Not_Reshown_When_Parent_Clicked()
{ {
var mainWindow = _session.FindElementByAccessibilityId("MainWindow"); var mainWindow = Session.FindElementByAccessibilityId("MainWindow");
// We don't use dispose to close the window here, because it seems that hiding and re-showing a window // We don't use dispose to close the window here, because it seems that hiding and re-showing a window
// causes Appium to think it's a different window. // causes Appium to think it's a different window.
@ -321,15 +296,15 @@ namespace Avalonia.IntegrationTests.Appium
hideButton.Click(); hideButton.Click();
var windows = _session.FindElementsByXPath("XCUIElementTypeWindow"); var windows = Session.FindElementsByXPath("XCUIElementTypeWindow");
Assert.Single(windows); Assert.Single(windows);
mainWindow.Click(); mainWindow.Click();
windows = _session.FindElementsByXPath("XCUIElementTypeWindow"); windows = Session.FindElementsByXPath("XCUIElementTypeWindow");
Assert.Single(windows); Assert.Single(windows);
_session.FindElementByAccessibilityId("RestoreAll").Click(); Session.FindElementByAccessibilityId("RestoreAll").Click();
// Close the window manually. // Close the window manually.
secondaryWindow = GetWindow("SecondaryWindow"); secondaryWindow = GetWindow("SecondaryWindow");
@ -352,9 +327,9 @@ namespace Avalonia.IntegrationTests.Appium
Assert.Equal(0, titleBar); Assert.Equal(0, titleBar);
secondaryWindow.FindElementByAccessibilityId("CurrentSystemDecorations").Click(); secondaryWindow.FindElementByAccessibilityId("CurrentSystemDecorations").Click();
_session.FindElementByAccessibilityId("SystemDecorationsNone").SendClick(); Session.FindElementByAccessibilityId("SystemDecorationsNone").SendClick();
secondaryWindow.FindElementByAccessibilityId("CurrentSystemDecorations").Click(); secondaryWindow.FindElementByAccessibilityId("CurrentSystemDecorations").Click();
_session.FindElementByAccessibilityId("SystemDecorationsFull").SendClick(); Session.FindElementByAccessibilityId("SystemDecorationsFull").SendClick();
titleBar = secondaryWindow.FindElementsByXPath("/*/XCUIElementTypeStaticText").Count; titleBar = secondaryWindow.FindElementsByXPath("/*/XCUIElementTypeStaticText").Count;
Assert.Equal(0, titleBar); Assert.Equal(0, titleBar);
@ -394,7 +369,7 @@ namespace Avalonia.IntegrationTests.Appium
if (decorations != SystemDecorations.Full) if (decorations != SystemDecorations.Full)
{ {
secondaryWindow.FindElementByAccessibilityId("CurrentSystemDecorations").Click(); secondaryWindow.FindElementByAccessibilityId("CurrentSystemDecorations").Click();
_session.FindElementByAccessibilityId("SystemDecorationsFull").SendClick(); Session.FindElementByAccessibilityId("SystemDecorationsFull").SendClick();
} }
} }
} }
@ -408,13 +383,13 @@ namespace Avalonia.IntegrationTests.Appium
SystemDecorations systemDecorations = SystemDecorations.Full, SystemDecorations systemDecorations = SystemDecorations.Full,
bool extendClientArea = false) bool extendClientArea = false)
{ {
var sizeTextBox = _session.FindElementByAccessibilityId("ShowWindowSize"); var sizeTextBox = Session.FindElementByAccessibilityId("ShowWindowSize");
var modeComboBox = _session.FindElementByAccessibilityId("ShowWindowMode"); var modeComboBox = Session.FindElementByAccessibilityId("ShowWindowMode");
var locationComboBox = _session.FindElementByAccessibilityId("ShowWindowLocation"); var locationComboBox = Session.FindElementByAccessibilityId("ShowWindowLocation");
var canResizeCheckBox = _session.FindElementByAccessibilityId("ShowWindowCanResize"); var canResizeCheckBox = Session.FindElementByAccessibilityId("ShowWindowCanResize");
var showButton = _session.FindElementByAccessibilityId("ShowWindow"); var showButton = Session.FindElementByAccessibilityId("ShowWindow");
var systemDecorationsComboBox = _session.FindElementByAccessibilityId("ShowWindowSystemDecorations"); var systemDecorationsComboBox = Session.FindElementByAccessibilityId("ShowWindowSystemDecorations");
var extendClientAreaCheckBox = _session.FindElementByAccessibilityId("ShowWindowExtendClientAreaToDecorationsHint"); var extendClientAreaCheckBox = Session.FindElementByAccessibilityId("ShowWindowExtendClientAreaToDecorationsHint");
if (size.HasValue) if (size.HasValue)
sizeTextBox.SendKeys($"{size.Value.Width}, {size.Value.Height}"); sizeTextBox.SendKeys($"{size.Value.Width}, {size.Value.Height}");
@ -422,13 +397,13 @@ namespace Avalonia.IntegrationTests.Appium
if (modeComboBox.GetComboBoxValue() != mode.ToString()) if (modeComboBox.GetComboBoxValue() != mode.ToString())
{ {
modeComboBox.Click(); modeComboBox.Click();
_session.FindElementByName(mode.ToString()).SendClick(); Session.FindElementByName(mode.ToString()).SendClick();
} }
if (locationComboBox.GetComboBoxValue() != location.ToString()) if (locationComboBox.GetComboBoxValue() != location.ToString())
{ {
locationComboBox.Click(); locationComboBox.Click();
_session.FindElementByName(location.ToString()).SendClick(); Session.FindElementByName(location.ToString()).SendClick();
} }
if (canResizeCheckBox.GetIsChecked() != canResize) if (canResizeCheckBox.GetIsChecked() != canResize)
@ -437,7 +412,7 @@ namespace Avalonia.IntegrationTests.Appium
if (systemDecorationsComboBox.GetComboBoxValue() != systemDecorations.ToString()) if (systemDecorationsComboBox.GetComboBoxValue() != systemDecorations.ToString())
{ {
systemDecorationsComboBox.Click(); systemDecorationsComboBox.Click();
_session.FindElementByName(systemDecorations.ToString()).SendClick(); Session.FindElementByName(systemDecorations.ToString()).SendClick();
} }
if (extendClientAreaCheckBox.GetIsChecked() != extendClientArea) if (extendClientAreaCheckBox.GetIsChecked() != extendClientArea)
@ -448,10 +423,7 @@ namespace Avalonia.IntegrationTests.Appium
private AppiumWebElement GetWindow(string identifier) private AppiumWebElement GetWindow(string identifier)
{ {
// The Avalonia a11y tree currently exposes two nested Window elements, this is a bug and should be fixed return Session.GetWindowById(identifier);
// but in the meantime use the `parent::' selector to return the parent "real" window.
return _session.FindElementByXPath(
$"XCUIElementTypeWindow//*[@identifier='{identifier}']/parent::XCUIElementTypeWindow");
} }
private int GetWindowOrder(string identifier) private int GetWindowOrder(string identifier)

Loading…
Cancel
Save