TrayIconTests.cs 5.5 KB

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