Browse Source
* Fix TopMost handling for Popups on Windows * Properly handle TopMost property changes * Call SetTopMost in PopupRoot constructor (similar to SetWindowManagerAddShadowHint) * Make sure the parent is focused when the popup is clicked directly to activate the app. * Move PopupsPage to IntegrationTestApp * Move Popup code to the XAML, making visual/logical management implicit * Add popup integration tests * Fix MenuTests+Default.Click_Grandchild by delaying popup activation * Kill input focus when input element on popup is focused element * try to fix tests * try to fix test v2 * Integration tests: ensure menus get closed after testing * run popup tests only on windows * WIP: Removing the hack to see if anything changes. * WIP: Removing the second hack. I'm guessing this will fix it. * fix to make transparent window integration app work on macOS * fix menu tests * Fix warning in MenuTests * Add a background to integration test popup --------- Co-authored-by: Maxwell Katz <maxkatz6@outlook.com> Co-authored-by: Steven Kirk <grokys@users.noreply.github.com> Co-authored-by: Julien Lebosquain <julien@lebosquain.net>pull/21628/head
committed by
GitHub
7 changed files with 371 additions and 22 deletions
@ -0,0 +1,70 @@ |
|||||
|
<UserControl xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
x:Class="IntegrationTestApp.Pages.PopupsPage"> |
||||
|
|
||||
|
<StackPanel Orientation="Vertical" Spacing="10"> |
||||
|
|
||||
|
<Button x:Name="DismissButton" |
||||
|
Content="Dismiss Button" /> |
||||
|
|
||||
|
<TextBlock Text="'Light Dismiss' Popup" /> |
||||
|
|
||||
|
<Button x:Name="ShowLightDismissPopup" |
||||
|
Content="Show Popup" Click="ButtonLightDismiss_OnClick" /> |
||||
|
<Popup x:Name="LightDismissPopup" |
||||
|
PlacementTarget="ShowLightDismissPopup" |
||||
|
Placement="Bottom" |
||||
|
IsLightDismissEnabled="True"> |
||||
|
<Panel Background="Gray" |
||||
|
Margin="10" |
||||
|
MinWidth="100" |
||||
|
MinHeight="30"> |
||||
|
<TextBox Text="Popup Content" |
||||
|
Name="LightDismissPopupContent" /> |
||||
|
</Panel> |
||||
|
</Popup> |
||||
|
|
||||
|
<TextBlock Text="Popup that stays open" /> |
||||
|
<Button x:Name="ShowStaysOpenPopup" |
||||
|
Content="Show Popup" Click="ButtonPopupStaysOpen_OnClick" /> |
||||
|
<Popup x:Name="StaysOpenPopup" |
||||
|
PlacementTarget="ShowStaysOpenPopup" |
||||
|
Placement="Bottom" |
||||
|
IsLightDismissEnabled="False"> |
||||
|
<StackPanel Background="Gray" |
||||
|
MinWidth="100" |
||||
|
MinHeight="30" |
||||
|
Spacing="20" |
||||
|
Orientation="Vertical"> |
||||
|
<TextBox Name="StaysOpenTextBox" |
||||
|
Width="100" |
||||
|
Text="Some text" /> |
||||
|
<Button Name="StaysOpenPopupCloseButton" |
||||
|
Click="StaysOpenPopupCloseButton_OnClick" |
||||
|
Content="Click to Close" /> |
||||
|
</StackPanel> |
||||
|
</Popup> |
||||
|
|
||||
|
<TextBlock Text="TopMost popup that stays open" /> |
||||
|
<Button x:Name="ShowTopMostPopup" |
||||
|
Content="Show Popup" Click="ButtonTopMostPopupStaysOpen" /> |
||||
|
<Popup x:Name="TopMostPopup" |
||||
|
Placement="Top" |
||||
|
PlacementTarget="ShowTopMostPopup" |
||||
|
IsLightDismissEnabled="False" |
||||
|
Topmost="True"> |
||||
|
<Panel MinWidth="100" |
||||
|
MinHeight="30" |
||||
|
Background="Gray"> |
||||
|
<Button Name="TopMostPopupCloseButton" |
||||
|
Click="TopMostPopupCloseButton_OnClick" |
||||
|
Content="Click to Close" /> |
||||
|
</Panel> |
||||
|
</Popup> |
||||
|
|
||||
|
<Button x:Name="OpenNewWindowButton" |
||||
|
Content="Open Regular New Window" |
||||
|
Click="OpenRegularNewWindow_Click" /> |
||||
|
</StackPanel> |
||||
|
|
||||
|
</UserControl> |
||||
@ -0,0 +1,48 @@ |
|||||
|
using Avalonia; |
||||
|
using Avalonia.Automation; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.Controls.Primitives.PopupPositioning; |
||||
|
using Avalonia.Interactivity; |
||||
|
using Avalonia.Layout; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
using Avalonia.Media; |
||||
|
|
||||
|
namespace IntegrationTestApp.Pages; |
||||
|
|
||||
|
public partial class PopupsPage : UserControl |
||||
|
{ |
||||
|
public PopupsPage() |
||||
|
{ |
||||
|
InitializeComponent(); |
||||
|
} |
||||
|
|
||||
|
private void ButtonLightDismiss_OnClick(object sender, RoutedEventArgs e) |
||||
|
{ |
||||
|
LightDismissPopup.Open(); |
||||
|
} |
||||
|
|
||||
|
private void ButtonPopupStaysOpen_OnClick(object sender, RoutedEventArgs e) |
||||
|
{ |
||||
|
StaysOpenPopup.Open(); |
||||
|
} |
||||
|
private void StaysOpenPopupCloseButton_OnClick(object? sender, RoutedEventArgs e) |
||||
|
{ |
||||
|
StaysOpenPopup.Close(); |
||||
|
} |
||||
|
|
||||
|
private void ButtonTopMostPopupStaysOpen(object sender, RoutedEventArgs e) |
||||
|
{ |
||||
|
TopMostPopup.Open(); |
||||
|
} |
||||
|
private void TopMostPopupCloseButton_OnClick(object? sender, RoutedEventArgs e) |
||||
|
{ |
||||
|
TopMostPopup.Close(); |
||||
|
} |
||||
|
|
||||
|
private void OpenRegularNewWindow_Click(object? sender, RoutedEventArgs e) |
||||
|
{ |
||||
|
var newWindow = new ShowWindowTest(); |
||||
|
newWindow.Show((Window)TopLevel.GetTopLevel(this)!); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,199 @@ |
|||||
|
using System.Threading; |
||||
|
using OpenQA.Selenium; |
||||
|
using OpenQA.Selenium.Interactions; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.IntegrationTests.Appium; |
||||
|
|
||||
|
[Collection("Default")] |
||||
|
public abstract class PopupsTests : TestBase |
||||
|
{ |
||||
|
private readonly bool _isOverlayPopups; |
||||
|
|
||||
|
protected PopupsTests(bool isOverlayPopups, DefaultAppFixture fixture) : base(fixture, "Popups") |
||||
|
{ |
||||
|
_isOverlayPopups = isOverlayPopups; |
||||
|
} |
||||
|
|
||||
|
[PlatformFact(TestPlatforms.Windows)] |
||||
|
public void LightDismiss_Popup_Should_Open_And_Close() |
||||
|
{ |
||||
|
// Open popup
|
||||
|
var button = Session.FindElementByAccessibilityId("ShowLightDismissPopup"); |
||||
|
button.Click(); |
||||
|
Thread.Sleep(500); |
||||
|
|
||||
|
// Assert - Popup is visible
|
||||
|
Assert.NotNull(Session.FindElementByAccessibilityId("LightDismissPopupContent")); |
||||
|
|
||||
|
// Act - Click outside to dismiss
|
||||
|
var dismissBorder = Session.FindElementByAccessibilityId("DismissButton"); |
||||
|
dismissBorder.Click(); |
||||
|
|
||||
|
Thread.Sleep(500); |
||||
|
|
||||
|
// Assert - Popup is closed
|
||||
|
Assert.Throws<WebDriverException>(() => |
||||
|
Session.FindElementByAccessibilityId("LightDismissPopupContent")); |
||||
|
} |
||||
|
|
||||
|
[PlatformFact(TestPlatforms.Windows)] |
||||
|
public void StaysOpen_Popup_Should_Stay_Open() |
||||
|
{ |
||||
|
// Open popup
|
||||
|
var button = Session.FindElementByAccessibilityId("ShowStaysOpenPopup"); |
||||
|
button.Click(); |
||||
|
Thread.Sleep(500); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
// Assert - Popup is visible
|
||||
|
Assert.NotNull(Session.FindElementByAccessibilityId("StaysOpenPopupCloseButton")); |
||||
|
|
||||
|
// Act - Click outside
|
||||
|
var dismissBorder = Session.FindElementByAccessibilityId("DismissButton"); |
||||
|
dismissBorder.Click(); |
||||
|
|
||||
|
Thread.Sleep(500); |
||||
|
|
||||
|
// Assert - Popup is still visible
|
||||
|
Assert.NotNull(Session.FindElementByAccessibilityId("StaysOpenPopupCloseButton")); |
||||
|
|
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
// Act - Close popup with button
|
||||
|
Session.FindElementByAccessibilityId("StaysOpenPopupCloseButton").Click(); |
||||
|
|
||||
|
Thread.Sleep(400); |
||||
|
|
||||
|
// Assert - Popup is closed
|
||||
|
Assert.Throws<WebDriverException>(() => |
||||
|
Session.FindElementByAccessibilityId("StaysOpenPopupCloseButton")); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[PlatformFact(TestPlatforms.Windows)] |
||||
|
public void StaysOpen_Popup_TextBox_Should_Be_Editable() |
||||
|
{ |
||||
|
// Open popup
|
||||
|
var button = Session.FindElementByAccessibilityId("ShowStaysOpenPopup"); |
||||
|
button.Click(); |
||||
|
Thread.Sleep(500); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
// Find and edit the TextBox
|
||||
|
var textBox = Session.FindElementByAccessibilityId("StaysOpenTextBox"); |
||||
|
textBox.Clear(); |
||||
|
textBox.SendKeys("New text value"); |
||||
|
Thread.Sleep(500); |
||||
|
|
||||
|
// Verify text was changed
|
||||
|
Assert.Equal("New text value", textBox.Text); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
// Cleanup - close popup
|
||||
|
Session.FindElementByAccessibilityId("StaysOpenPopupCloseButton").Click(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[PlatformFact(TestPlatforms.Windows)] |
||||
|
public void TopMost_Popup_Should_Stay_Above_Other_Windows() |
||||
|
{ |
||||
|
// It's not possible to test overlay topmost with other windows.
|
||||
|
if (_isOverlayPopups) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var staysOpenPopup = Session.FindElementByAccessibilityId("ShowTopMostPopup"); |
||||
|
var mainWindowHandle = Session.CurrentWindowHandle; |
||||
|
|
||||
|
// Show topmost popup.
|
||||
|
staysOpenPopup.Click(); |
||||
|
Assert.NotNull(Session.FindElementByAccessibilityId("TopMostPopupCloseButton")); |
||||
|
|
||||
|
var hasClosedPopup = false; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
// Open a child window.
|
||||
|
using var _ = Session.FindElementByAccessibilityId("OpenNewWindowButton").OpenWindowWithClick(); |
||||
|
Thread.Sleep(500); |
||||
|
|
||||
|
// Force window to front by maximizing child window.
|
||||
|
Session.FindElementByAccessibilityId("CurrentWindowState").SendClick(); |
||||
|
Session.FindElementByAccessibilityId("WindowStateMaximized").SendClick(); |
||||
|
|
||||
|
// Switch back to the mainwindow context and verify tooltip is still accessible.
|
||||
|
Session.SwitchTo().Window(mainWindowHandle); |
||||
|
Assert.NotNull(Session.FindElementByAccessibilityId("TopMostPopupCloseButton")); |
||||
|
|
||||
|
// Verify we can still interact with the popup by closing it via button.
|
||||
|
Session.FindElementByAccessibilityId("TopMostPopupCloseButton").Click(); |
||||
|
|
||||
|
// Verify popup closed
|
||||
|
Assert.Throws<WebDriverException>(() => |
||||
|
Session.FindElementByAccessibilityId("TopMostPopupCloseButton")); |
||||
|
hasClosedPopup = true; |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
if (!hasClosedPopup) |
||||
|
{ |
||||
|
Session.FindElementByAccessibilityId("TopMostPopupCloseButton").Click(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[PlatformFact(TestPlatforms.Windows)] |
||||
|
public void Non_TopMost_Popup_Does_Not_Stay_Above_Other_Windows() |
||||
|
{ |
||||
|
var topmostButton = Session.FindElementByAccessibilityId("ShowStaysOpenPopup"); |
||||
|
var mainWindowHandle = Session.CurrentWindowHandle; |
||||
|
|
||||
|
// Show topmost popup.
|
||||
|
topmostButton.Click(); |
||||
|
Assert.NotNull(Session.FindElementByAccessibilityId("StaysOpenPopupCloseButton")); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
// Open a child window.
|
||||
|
var newWindowButton = Session.FindElementByAccessibilityId("OpenNewWindowButton"); |
||||
|
using var _ = newWindowButton.OpenWindowWithClick(); |
||||
|
|
||||
|
// Force window to front by maximizing child window.
|
||||
|
Session.FindElementByAccessibilityId("CurrentWindowState").SendClick(); |
||||
|
Session.FindElementByAccessibilityId("WindowStateMaximized").SendClick(); |
||||
|
|
||||
|
// Switch back to the mainwindow context and verify tooltip is still accessible.
|
||||
|
Session.SwitchTo().Window(mainWindowHandle); |
||||
|
Assert.NotNull(Session.FindElementByAccessibilityId("StaysOpenPopupCloseButton")); |
||||
|
|
||||
|
// Verify we cannot interact with the popup by attempting closing it via button.
|
||||
|
Session.FindElementByAccessibilityId("StaysOpenPopupCloseButton").Click(); |
||||
|
|
||||
|
// Verify popup is still accessible.
|
||||
|
Assert.NotNull(Session.FindElementByAccessibilityId("StaysOpenPopupCloseButton")); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
// At this point secondary window should be already closed. And safe to close the popup.
|
||||
|
Session.FindElementByAccessibilityId("StaysOpenPopupCloseButton").Click(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Collection("Default")] |
||||
|
public class Default : PopupsTests |
||||
|
{ |
||||
|
public Default(DefaultAppFixture fixture) : base(false, fixture) { } |
||||
|
} |
||||
|
|
||||
|
[Collection("OverlayPopups")] |
||||
|
public class OverlayPopups : PopupsTests |
||||
|
{ |
||||
|
public OverlayPopups(OverlayPopupsAppFixture fixture) : base(true, fixture) { } |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue