Public Access
173 lines
6.5 KiB
C#
173 lines
6.5 KiB
C#
using MeetingAssistant.LaunchProfiles;
|
|
using MeetingAssistant.Recording;
|
|
using MeetingAssistant.Taskbar;
|
|
using System.Drawing;
|
|
using System.Runtime.Versioning;
|
|
|
|
namespace MeetingAssistant.Tests;
|
|
|
|
public sealed class TaskbarIconTests
|
|
{
|
|
[Fact]
|
|
public void IdleMenuOffersStartForEachConfiguredProfile()
|
|
{
|
|
var menu = MeetingTaskbarMenuBuilder.Build(
|
|
Status(),
|
|
[Profile("default", "Ctrl+Alt+M"), Profile("english", "Ctrl+Alt+L")]);
|
|
|
|
Assert.Equal(RecordingProcessState.Idle, menu.State);
|
|
Assert.Contains(menu.Items, item =>
|
|
item.Action == MeetingTaskbarAction.EditRules &&
|
|
item.Text == "Settings and logs");
|
|
Assert.Contains(menu.Items, item =>
|
|
item.Action == MeetingTaskbarAction.StartRecording &&
|
|
item.ProfileName == "default" &&
|
|
item.Text == "Start meeting recording (default)\tCtrl+Alt+M");
|
|
Assert.Contains(menu.Items, item =>
|
|
item.Action == MeetingTaskbarAction.StartRecording &&
|
|
item.ProfileName == "english" &&
|
|
item.Text == "Start meeting recording (english)\tCtrl+Alt+L");
|
|
Assert.DoesNotContain(menu.Items, item => item.Action == MeetingTaskbarAction.StopRecording);
|
|
Assert.DoesNotContain(menu.Items, item => item.Action == MeetingTaskbarAction.AbortRecording);
|
|
}
|
|
|
|
[Fact]
|
|
public void RecordingMenuOffersStopAbortAndOtherProfileSwitches()
|
|
{
|
|
var menu = MeetingTaskbarMenuBuilder.Build(
|
|
Status(isRecording: true, state: RecordingProcessState.Recording, profile: "default"),
|
|
[Profile("default", "Ctrl+Alt+M"), Profile("english", "Ctrl+Alt+L"), Profile("french", "Ctrl+Alt+F")]);
|
|
|
|
Assert.Equal(RecordingProcessState.Recording, menu.State);
|
|
Assert.Contains(menu.Items, item => item.Action == MeetingTaskbarAction.StopRecording);
|
|
Assert.Contains(menu.Items, item => item.Action == MeetingTaskbarAction.AbortRecording);
|
|
Assert.Contains(menu.Items, item =>
|
|
item.Action == MeetingTaskbarAction.SwitchProfile &&
|
|
item.ProfileName == "english" &&
|
|
item.Text == "Switch to english\tCtrl+Alt+L");
|
|
Assert.Contains(menu.Items, item =>
|
|
item.Action == MeetingTaskbarAction.SwitchProfile &&
|
|
item.ProfileName == "french" &&
|
|
item.Text == "Switch to french\tCtrl+Alt+F");
|
|
Assert.DoesNotContain(menu.Items, item =>
|
|
item.Action == MeetingTaskbarAction.SwitchProfile &&
|
|
item.ProfileName == "default");
|
|
Assert.DoesNotContain(menu.Items, item => item.Action == MeetingTaskbarAction.StartRecording);
|
|
}
|
|
|
|
[Fact]
|
|
public void ProcessingMenuShowsSummarizingButAllowsStartingNewRecordings()
|
|
{
|
|
var menu = MeetingTaskbarMenuBuilder.Build(
|
|
Status(state: RecordingProcessState.Summarizing, profile: "default"),
|
|
[Profile("default"), Profile("english")]);
|
|
|
|
Assert.Equal(RecordingProcessState.Summarizing, menu.State);
|
|
Assert.Contains(menu.Items, item =>
|
|
item.Action == MeetingTaskbarAction.StartRecording &&
|
|
item.ProfileName == "default");
|
|
Assert.Contains(menu.Items, item =>
|
|
item.Action == MeetingTaskbarAction.StartRecording &&
|
|
item.ProfileName == "english");
|
|
Assert.DoesNotContain(menu.Items, item => item.Action == MeetingTaskbarAction.StopRecording);
|
|
Assert.DoesNotContain(menu.Items, item => item.Action == MeetingTaskbarAction.AbortRecording);
|
|
}
|
|
|
|
[Fact]
|
|
public void RecordingStateWinsOverOlderSummarizingWork()
|
|
{
|
|
var menu = MeetingTaskbarMenuBuilder.Build(
|
|
Status(isRecording: true, state: RecordingProcessState.Recording, profile: "english"),
|
|
[Profile("default"), Profile("english")]);
|
|
|
|
Assert.Equal(RecordingProcessState.Recording, menu.State);
|
|
}
|
|
|
|
[Fact]
|
|
[SupportedOSPlatform("windows")]
|
|
public void TaskbarIconGlyphsAreVisuallyCentered()
|
|
{
|
|
foreach (var state in new[]
|
|
{
|
|
RecordingProcessState.Idle,
|
|
RecordingProcessState.Recording,
|
|
RecordingProcessState.Summarizing
|
|
})
|
|
{
|
|
using var bitmap = TaskbarIconRenderer.RenderBitmap(state);
|
|
var bounds = GetVisibleGlyphBounds(bitmap);
|
|
|
|
Assert.True(
|
|
IsCentered(GetCenter(bounds.Left, bounds.Right)),
|
|
$"{state} glyph is horizontally off-center: {bounds}");
|
|
Assert.True(
|
|
IsCentered(GetCenter(bounds.Top, bounds.Bottom)),
|
|
$"{state} glyph is vertically off-center: {bounds}");
|
|
}
|
|
}
|
|
|
|
private static LaunchProfile Profile(string name, string hotkey = "")
|
|
{
|
|
return new LaunchProfile(name, new MeetingAssistantOptions
|
|
{
|
|
Hotkey =
|
|
{
|
|
Toggle = hotkey
|
|
}
|
|
});
|
|
}
|
|
|
|
private static RecordingStatus Status(
|
|
bool isRecording = false,
|
|
RecordingProcessState state = RecordingProcessState.Idle,
|
|
string? profile = null)
|
|
{
|
|
return new RecordingStatus(
|
|
isRecording,
|
|
state == RecordingProcessState.Idle ? null : "transcript.md",
|
|
state == RecordingProcessState.Idle ? null : "meeting.md",
|
|
state == RecordingProcessState.Idle ? null : "context.md",
|
|
state == RecordingProcessState.Idle ? null : "summary.md",
|
|
state,
|
|
profile);
|
|
}
|
|
|
|
[SupportedOSPlatform("windows")]
|
|
private static Rectangle GetVisibleGlyphBounds(Bitmap bitmap)
|
|
{
|
|
var left = bitmap.Width;
|
|
var top = bitmap.Height;
|
|
var right = -1;
|
|
var bottom = -1;
|
|
for (var y = 0; y < bitmap.Height; y++)
|
|
{
|
|
for (var x = 0; x < bitmap.Width; x++)
|
|
{
|
|
var pixel = bitmap.GetPixel(x, y);
|
|
if (pixel.A < 32 || pixel.GetBrightness() < 0.58f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
left = Math.Min(left, x);
|
|
top = Math.Min(top, y);
|
|
right = Math.Max(right, x);
|
|
bottom = Math.Max(bottom, y);
|
|
}
|
|
}
|
|
|
|
Assert.True(right >= left && bottom >= top, "Expected the icon to contain a visible white glyph.");
|
|
return Rectangle.FromLTRB(left, top, right + 1, bottom + 1);
|
|
}
|
|
|
|
private static double GetCenter(int start, int end)
|
|
{
|
|
return (start + end) / 2.0;
|
|
}
|
|
|
|
private static bool IsCentered(double center)
|
|
{
|
|
return center is >= 7.25 and <= 8.75;
|
|
}
|
|
}
|