TrayIconTests.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Threading;
  5. using OpenQA.Selenium;
  6. using OpenQA.Selenium.Appium;
  7. using OpenQA.Selenium.Interactions;
  8. using Xunit;
  9. namespace Avalonia.IntegrationTests.Appium;
  10. [Collection("Default")]
  11. public class TrayIconTests : TestBase, IDisposable
  12. {
  13. private readonly AppiumDriver? _rootSession;
  14. private const string TrayIconName = "IntegrationTestApp TrayIcon";
  15. public TrayIconTests(DefaultAppFixture fixture)
  16. : base(fixture, "Desktop")
  17. {
  18. // "Root" is a special name for windows the desktop session, that has access to task bar.
  19. if (OperatingSystem.IsWindows())
  20. {
  21. _rootSession = fixture.CreateNestedSession("Root");
  22. }
  23. }
  24. // Left click is only supported on Windows.
  25. [PlatformFact(TestPlatforms.Windows, Skip = "Flaky test")]
  26. public void Should_Handle_Left_Click()
  27. {
  28. var avaloniaTrayIconButton = GetTrayIconButton(_rootSession ?? Session, TrayIconName);
  29. Assert.NotNull(avaloniaTrayIconButton);
  30. avaloniaTrayIconButton.SendClick();
  31. Thread.Sleep(2000);
  32. var checkBox = Session.FindElementByAccessibilityId("TrayIconClicked");
  33. Assert.True(checkBox.GetIsChecked());
  34. }
  35. [Fact(Skip = "Flaky test")]
  36. public void Should_Handle_Context_Menu_Item_Click()
  37. {
  38. var avaloniaTrayIconButton = GetTrayIconButton(_rootSession ?? Session, TrayIconName);
  39. Assert.NotNull(avaloniaTrayIconButton);
  40. var contextMenu = ShowAndGetTrayMenu(avaloniaTrayIconButton, TrayIconName);
  41. Assert.NotNull(contextMenu);
  42. var menuItem = contextMenu.FindElementByName("Raise Menu Clicked");
  43. menuItem.SendClick();
  44. Thread.Sleep(2000);
  45. var checkBox = Session.FindElementByAccessibilityId("TrayIconMenuClicked");
  46. Assert.True(checkBox.GetIsChecked());
  47. }
  48. [Fact(Skip = "Flaky test")]
  49. public void Can_Toggle_TrayIcon_Visibility()
  50. {
  51. var avaloniaTrayIconButton = GetTrayIconButton(_rootSession ?? Session, TrayIconName);
  52. Assert.NotNull(avaloniaTrayIconButton);
  53. var toggleButton = Session.FindElementByAccessibilityId("ToggleTrayIconVisible");
  54. toggleButton.SendClick();
  55. avaloniaTrayIconButton = GetTrayIconButton(_rootSession ?? Session, TrayIconName);
  56. Assert.Null(avaloniaTrayIconButton);
  57. toggleButton.SendClick();
  58. avaloniaTrayIconButton = GetTrayIconButton(_rootSession ?? Session, TrayIconName);
  59. Assert.NotNull(avaloniaTrayIconButton);
  60. }
  61. private static AppiumWebElement? GetTrayIconButton(AppiumDriver session, string trayIconName)
  62. {
  63. if (OperatingSystem.IsWindows())
  64. {
  65. var taskBar = session.FindElementsByClassName("Shell_TrayWnd")
  66. .FirstOrDefault() ?? throw new InvalidOperationException("Couldn't find Taskbar on current system.");
  67. if (TryToGetIcon(taskBar, trayIconName) is { } trayIcon)
  68. {
  69. return trayIcon;
  70. }
  71. else
  72. {
  73. // Add a sleep here, as previous test might still run popup closing animation.
  74. Thread.Sleep(1000);
  75. // win11: SystemTrayIcon
  76. // win10: Notification Chevron
  77. var trayIconsButton = taskBar.FindElementsByAccessibilityId("SystemTrayIcon").FirstOrDefault()
  78. ?? taskBar.FindElementsByName("Notification Chevron").FirstOrDefault()
  79. ?? throw new InvalidOperationException("SystemTrayIcon cannot be found.");
  80. trayIconsButton.Click();
  81. // win11: TopLevelWindowForOverflowXamlIsland
  82. // win10: NotifyIconOverflowWindow
  83. var trayIconsFlyout = session.FindElementsByClassName("TopLevelWindowForOverflowXamlIsland").FirstOrDefault()
  84. ?? session.FindElementsByClassName("NotifyIconOverflowWindow").FirstOrDefault()
  85. ?? throw new InvalidOperationException("System tray overflow window cannot be found.");
  86. return TryToGetIcon(trayIconsFlyout, trayIconName);
  87. }
  88. static AppiumWebElement? TryToGetIcon(AppiumWebElement parent, string trayIconName) =>
  89. parent.FindElementsByName(trayIconName).LastOrDefault()
  90. // Some icons (including Avalonia) for some reason include leading whitespace in their name.
  91. // Couldn't find any info on that, which is weird.
  92. ?? parent.FindElementsByName(" " + trayIconName).LastOrDefault();
  93. }
  94. if (OperatingSystem.IsMacOS())
  95. {
  96. return session.FindElementsByXPath("//XCUIElementTypeStatusItem").FirstOrDefault();
  97. }
  98. throw new PlatformNotSupportedException();
  99. }
  100. private static AppiumWebElement ShowAndGetTrayMenu(AppiumWebElement trayIcon, string trayIconName)
  101. {
  102. if (OperatingSystem.IsWindows())
  103. {
  104. var session = (AppiumDriver)trayIcon.WrappedDriver;
  105. new Actions(trayIcon.WrappedDriver).ContextClick(trayIcon).Perform();
  106. Thread.Sleep(1000);
  107. return session.FindElementByXPath($"//Window[@AutomationId='AvaloniaTrayPopupRoot_{trayIconName}']");
  108. }
  109. else
  110. {
  111. trayIcon.Click();
  112. return trayIcon.FindElementByXPath("//XCUIElementTypeStatusItem/XCUIElementTypeMenu");
  113. }
  114. }
  115. public void Dispose()
  116. {
  117. _rootSession?.Dispose();
  118. }
  119. }