Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Pointframe.AutomationTests/Smoke/LaunchModeSmokeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ public void AboutLaunchMode_OpensAboutWindowAndCloses()
app.WaitForExit();
}

[Fact]
[Trait("Category", "DesktopAutomation")]
public void LibraryLaunchMode_OpensLibraryWindowAndCloses()
{
_fixture.SeedSettings(autoSaveScreenshots: false);

using var app = AutomationApp.Launch("--automation-open-library", _fixture.CreateEnvironmentVariables());
Assert.Equal(AutomationIds.LibraryWindowRoot, app.MainWindowAutomationId);
Assert.NotNull(app.FindRequiredElement(AutomationIds.LibraryWindowSearch));

app.CloseMainWindow();
app.WaitForExit();
}

[Fact]
[Trait("Category", "DesktopAutomation")]
public void SampleOverlayLaunchMode_OpensOpenedImageOverlayAndCloses()
Expand Down
3 changes: 3 additions & 0 deletions Pointframe.AutomationTests/Support/AutomationIds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ internal static class AutomationIds
{
public const string AboutWindowRoot = "AboutWindow.Root";
public const string AboutWindowClose = "AboutWindow.Close";
public const string LibraryWindowRoot = "LibraryWindow.Root";
public const string LibraryWindowSearch = "LibraryWindow.Search";
public const string LibraryWindowClose = "LibraryWindow.Close";
public const string SettingsWindowRoot = "SettingsWindow.Root";
public const string SettingsWindowAutoSaveScreenshots = "SettingsWindow.AutoSaveScreenshots";
public const string SettingsWindowCancel = "SettingsWindow.Cancel";
Expand Down
16 changes: 11 additions & 5 deletions Pointframe.Tests/Automation/AutomationLaunchOptionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ namespace Pointframe.Tests.Automation;
public sealed class AutomationLaunchOptionsTests
{
[Theory]
[InlineData("--automation-open-settings", true, false, false, false, false)]
[InlineData("--automation-open-about", false, true, false, false, false)]
[InlineData("--automation-open-sample-overlay", false, false, true, false, false)]
[InlineData("--automation-open-sample-recording-overlay", false, false, false, true, false)]
[InlineData("--automation-open-tray-sample-overlay", false, false, false, false, true)]
[InlineData("--automation-open-settings", true, false, false, false, false, false)]
[InlineData("--automation-open-about", false, true, false, false, false, false)]
[InlineData("--automation-open-library", false, false, true, false, false, false)]
[InlineData("--automation-open-sample-overlay", false, false, false, true, false, false)]
[InlineData("--automation-open-sample-recording-overlay", false, false, false, false, true, false)]
[InlineData("--automation-open-tray-sample-overlay", false, false, false, false, false, true)]
public void Parse_WhenSingleAutomationArgumentIsPresent_SetsMatchingFlag(
string argument,
bool openSettingsWindow,
bool openAboutWindow,
bool openLibraryWindow,
bool openSampleOverlayWindow,
bool openSampleRecordingOverlayWindow,
bool openTraySampleOverlayWindow)
Expand All @@ -23,6 +25,7 @@ public void Parse_WhenSingleAutomationArgumentIsPresent_SetsMatchingFlag(

Assert.Equal(openSettingsWindow, options.OpenSettingsWindow);
Assert.Equal(openAboutWindow, options.OpenAboutWindow);
Assert.Equal(openLibraryWindow, options.OpenLibraryWindow);
Assert.Equal(openSampleOverlayWindow, options.OpenSampleOverlayWindow);
Assert.Equal(openSampleRecordingOverlayWindow, options.OpenSampleRecordingOverlayWindow);
Comment on lines 26 to 30
Assert.Equal(openTraySampleOverlayWindow, options.OpenTraySampleOverlayWindow);
Expand All @@ -35,11 +38,13 @@ public void Parse_WhenMultipleAutomationArgumentsArePresent_SetsAllMatchingFlags
var options = AutomationLaunchOptions.Parse(
[
"--automation-open-settings",
"--automation-open-library",
"--automation-open-sample-recording-overlay",
"--automation-open-tray-sample-overlay",
]);
Comment on lines 38 to 44

Assert.True(options.OpenSettingsWindow);
Assert.True(options.OpenLibraryWindow);
Assert.True(options.OpenSampleRecordingOverlayWindow);
Assert.True(options.OpenTraySampleOverlayWindow);
Assert.True(options.IsAutomationMode);
Expand All @@ -52,6 +57,7 @@ public void Parse_WhenNoAutomationArgumentsArePresent_DisablesAutomationMode()

Assert.False(options.OpenSettingsWindow);
Assert.False(options.OpenAboutWindow);
Assert.False(options.OpenLibraryWindow);
Assert.False(options.OpenSampleOverlayWindow);
Assert.False(options.OpenSampleRecordingOverlayWindow);
Assert.False(options.OpenTraySampleOverlayWindow);
Expand Down
164 changes: 156 additions & 8 deletions Pointframe.Tests/Services/TrayIconManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,106 @@ public void CreateTrayMenuItem_AssignsHeaderAndClickHandler()
});
}

[Fact]
public void CreateTrayContextMenu_UsesExpectedSectionOrderAndLabels()
{
StaTestHelper.Run(() =>
{
var manager = CreateManager();

var contextMenu = (System.Windows.Controls.ContextMenu)InvokePrivateResult(manager, "CreateTrayContextMenu");
var headers = GetTopLevelHeaders(contextMenu);

Assert.Equal(
[
"New Snip",
"Whole Screen Snip",
"Clean Window Snip",
"Open Image...",
"<separator>",
"Recent Captures",
"Recent Recordings",
"<separator>",
"Library",
"Open Folders",
"<separator>",
"Settings",
"Check for Updates",
"About",
"<separator>",
"Quit Pointframe",
],
headers);
});
}

[Fact]
public void CreateTrayContextMenu_AssignsIconsToTopLevelCommands()
{
StaTestHelper.Run(() =>
{
var manager = CreateManager();

var contextMenu = (System.Windows.Controls.ContextMenu)InvokePrivateResult(manager, "CreateTrayContextMenu");

AssertMenuItemHasIcon(contextMenu, "New Snip");
AssertMenuItemHasIcon(contextMenu, "Whole Screen Snip");
AssertMenuItemHasIcon(contextMenu, "Clean Window Snip");
AssertMenuItemHasIcon(contextMenu, "Open Image...");
AssertMenuItemHasIcon(contextMenu, "Recent Captures");
AssertMenuItemHasIcon(contextMenu, "Recent Recordings");
AssertMenuItemHasIcon(contextMenu, "Library");
AssertMenuItemHasIcon(contextMenu, "Open Folders");
AssertMenuItemHasIcon(contextMenu, "Settings");
AssertMenuItemHasIcon(contextMenu, "Check for Updates");
AssertMenuItemHasIcon(contextMenu, "About");
AssertMenuItemHasIcon(contextMenu, "Quit Pointframe");
});
}

[Fact]
public void HandleUpdateAvailable_UpdatesTrayMenuHeaderToInstall()
{
StaTestHelper.Run(() =>
{
var manager = CreateManager();
var contextMenu = (System.Windows.Controls.ContextMenu)InvokePrivateResult(manager, "CreateTrayContextMenu");
var checkForUpdatesItem = FindTopLevelMenuItem(contextMenu, "Check for Updates");
Assert.NotNull(checkForUpdatesItem);

manager.HandleUpdateAvailable(new UpdateCheckResult(true, new Version(2, 4, 1), "https://example.com/download"));

Assert.Equal("Install Update (2.4.1)", checkForUpdatesItem!.Header?.ToString());
});
}

[Fact]
public void CheckForUpdates_Click_WhenPendingUpdate_InstallsPendingAndResetsHeader()
{
StaTestHelper.RunAsync(async () =>
{
var autoUpdateMock = new Mock<IAutoUpdateService>();
autoUpdateMock
.Setup(service => service.ConfirmAndInstall(It.IsAny<UpdateCheckResult>()))
.Returns(Task.CompletedTask);
var manager = CreateManager(autoUpdate: autoUpdateMock.Object);

var contextMenu = (System.Windows.Controls.ContextMenu)InvokePrivateResult(manager, "CreateTrayContextMenu");
var checkForUpdatesItem = FindTopLevelMenuItem(contextMenu, "Check for Updates");
Assert.NotNull(checkForUpdatesItem);

var pendingUpdate = new UpdateCheckResult(true, new Version(3, 1, 0), "https://example.com/download");
manager.HandleUpdateAvailable(pendingUpdate);

InvokePrivate(manager, "CheckForUpdates_Click", checkForUpdatesItem!, new RoutedEventArgs());
await WaitForCondition(() => checkForUpdatesItem!.IsEnabled, TimeSpan.FromSeconds(3));

autoUpdateMock.Verify(service => service.ConfirmAndInstall(pendingUpdate), Times.Once);
Assert.Equal("Check for Updates", checkForUpdatesItem.Header?.ToString());
Assert.Null(GetField(manager, "_pendingUpdate"));
});
}

[Fact]
public void OpenRecentRecordingFolder_Click_WithValidTag_OpensFolder()
{
Expand Down Expand Up @@ -471,8 +571,8 @@ public void RebuildRecentCapturesMenu_WhenEmpty_ShowsOpenSnipsFolderShortcut()

InvokePrivate(manager, "RebuildRecentCapturesMenu");

Assert.True(HasMenuItemHeader(menuItem, "No recent captures"));
Assert.True(HasMenuItemHeader(menuItem, "Open Snips folder"));
Assert.True(HasMenuItemHeader(menuItem, "No Recent Captures"));
Assert.True(HasMenuItemHeader(menuItem, "Open Snips Folder"));
});
}

Expand All @@ -490,8 +590,8 @@ public void RebuildRecentCapturesMenu_WhenNotEmpty_HidesOpenSnipsFolderShortcut(

InvokePrivate(manager, "RebuildRecentCapturesMenu");

Assert.False(HasMenuItemHeader(menuItem, "Open Snips folder"));
Assert.True(HasMenuItemHeader(menuItem, "Clear recent captures"));
Assert.False(HasMenuItemHeader(menuItem, "Open Snips Folder"));
Assert.True(HasMenuItemHeader(menuItem, "Clear Recent Captures"));
});
}

Expand All @@ -506,8 +606,8 @@ public void RebuildRecentRecordingsMenu_WhenEmpty_ShowsOpenVideosFolderShortcut(

InvokePrivate(manager, "RebuildRecentRecordingsMenu");

Assert.True(HasMenuItemHeader(menuItem, "No recent recordings"));
Assert.True(HasMenuItemHeader(menuItem, "Open Videos folder"));
Assert.True(HasMenuItemHeader(menuItem, "No Recent Recordings"));
Assert.True(HasMenuItemHeader(menuItem, "Open Videos Folder"));
});
}

Expand All @@ -527,8 +627,8 @@ public void RebuildRecentRecordingsMenu_WhenNotEmpty_HidesOpenVideosFolderShortc

InvokePrivate(manager, "RebuildRecentRecordingsMenu");

Assert.False(HasMenuItemHeader(menuItem, "Open Videos folder"));
Assert.True(HasMenuItemHeader(menuItem, "Clear recent recordings"));
Assert.False(HasMenuItemHeader(menuItem, "Open Videos Folder"));
Assert.True(HasMenuItemHeader(menuItem, "Clear Recent Recordings"));
});
}

Expand Down Expand Up @@ -585,6 +685,13 @@ private static void InvokePrivate(object target, string methodName, params objec
method.Invoke(target, args);
}

private static object InvokePrivateResult(object target, string methodName, params object[] args)
{
var method = target.GetType().GetMethod(methodName, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
Assert.NotNull(method);
return method.Invoke(target, args)!;
}

private static object? GetField(object target, string fieldName)
{
var field = target.GetType().GetField(fieldName, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
Expand Down Expand Up @@ -633,4 +740,45 @@ private static bool HasMenuItemHeader(System.Windows.Controls.MenuItem menuItem,

return false;
}

private static List<string> GetTopLevelHeaders(System.Windows.Controls.ContextMenu contextMenu)
{
var headers = new List<string>();
foreach (var item in contextMenu.Items)
{
if (item is System.Windows.Controls.Separator)
{
headers.Add("<separator>");
continue;
}

if (item is System.Windows.Controls.MenuItem menuItem)
{
headers.Add(menuItem.Header?.ToString() ?? string.Empty);
}
}

return headers;
}

private static System.Windows.Controls.MenuItem? FindTopLevelMenuItem(System.Windows.Controls.ContextMenu contextMenu, string header)
{
foreach (var item in contextMenu.Items)
{
if (item is System.Windows.Controls.MenuItem menuItem &&
string.Equals(menuItem.Header?.ToString(), header, StringComparison.Ordinal))
{
return menuItem;
}
}

return null;
}

private static void AssertMenuItemHasIcon(System.Windows.Controls.ContextMenu contextMenu, string header)
{
var item = FindTopLevelMenuItem(contextMenu, header);
Assert.NotNull(item);
Assert.NotNull(item!.Icon);
}
}
Loading
Loading