added tray menu

This commit is contained in:
2026-08-12 04:59:22 +05:00
parent 08192d317e
commit a0d3098fe4
21 changed files with 1327 additions and 42 deletions
+44 -14
View File
@@ -3,6 +3,7 @@ using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using CursorLang.Services;
namespace CursorLang.Tests;
@@ -25,6 +26,9 @@ public sealed class EndToEndTests
private static readonly TimeSpan StartTimeout = TimeSpan.FromSeconds(30);
private static readonly TimeSpan ExitTimeout = TimeSpan.FromSeconds(15);
/// <summary>How long "the application went on working" is worth watching for.</summary>
private static readonly TimeSpan StayTimeout = TimeSpan.FromSeconds(3);
[Fact]
public void The_application_starts_and_shows_the_settings_window()
{
@@ -34,6 +38,22 @@ public sealed class EndToEndTests
Assert.False(launch.Process.HasExited);
}
/// <summary>
/// Started by Windows itself, the application goes straight to the tray: the
/// user asked for it to be there, not for a window to greet them.
/// </summary>
[Fact]
public void A_launch_by_Windows_shows_no_window()
{
using Launch launch = Launch.Start(StartupLaunch.Argument);
// Nothing is expected to appear, so the wait is for the whole time
Assert.False(launch.Process.WaitForExit(StayTimeout), "the application ended by itself");
launch.Process.Refresh();
Assert.Equal(IntPtr.Zero, launch.Process.MainWindowHandle);
}
// A second run raises no second window but shows the window of the running one
[Fact]
public void The_second_run_ends_by_itself()
@@ -50,15 +70,19 @@ public sealed class EndToEndTests
Assert.False(launch.Process.HasExited);
}
// The way out of the application is the tray menu alone: the close button of
// the window merely puts the window away
[Fact]
public void Closing_the_window_ends_the_application()
public void Closing_the_window_leaves_the_application_in_the_tray()
{
using Launch launch = Launch.Start();
launch.WaitForWindow();
Assert.True(launch.Process.CloseMainWindow(), "the window did not accept the request to close");
Assert.True(launch.Process.WaitForExit(ExitTimeout), "the application did not end after the window closed");
Assert.Equal(0, launch.Process.ExitCode);
Assert.False(launch.Process.WaitForExit(StayTimeout), "the application ended together with its window");
launch.Process.Refresh();
Assert.Equal(IntPtr.Zero, launch.Process.MainWindowHandle);
}
/// <summary>A started application that shuts down together with the check.</summary>
@@ -79,7 +103,7 @@ public sealed class EndToEndTests
internal Process Process { get; }
/// <summary>Starts the application first — making sure the place is free.</summary>
internal static Launch Start()
internal static Launch Start(params string[] arguments)
{
if (!HasInteractiveDesktop())
{
@@ -91,11 +115,11 @@ public sealed class EndToEndTests
Assert.Skip("The application is already running — this check keeps out of someone else's run");
}
return new Launch(StartProcess());
return new Launch(StartProcess(arguments));
}
/// <summary>Starts the application the way the user does.</summary>
internal static Process StartProcess()
/// <summary>Starts the application the way the user — or Windows — does.</summary>
internal static Process StartProcess(params string[] arguments)
{
string path = ExecutablePath();
@@ -104,7 +128,14 @@ public sealed class EndToEndTests
Assert.Skip($"The application is not built: {path}");
}
return Process.Start(new ProcessStartInfo(path) { UseShellExecute = true })!;
var start = new ProcessStartInfo(path) { UseShellExecute = true };
foreach (string argument in arguments)
{
start.ArgumentList.Add(argument);
}
return Process.Start(start)!;
}
/// <summary>
@@ -171,12 +202,11 @@ public sealed class EndToEndTests
{
if (!Process.HasExited)
{
// The polite way first — that way the app gets to save its settings
if (!Process.CloseMainWindow() || !Process.WaitForExit(ExitTimeout))
{
Process.Kill(entireProcessTree: true);
Process.WaitForExit(ExitTimeout);
}
// Asking the window to close would only put it away into the
// tray, and the exit lives in a menu no test can reach. The
// settings are saved as they change, so nothing is lost here
Process.Kill(entireProcessTree: true);
Process.WaitForExit(ExitTimeout);
}
}
catch (InvalidOperationException)