Public Access
Add taskbar controls and summary oneliner
This commit is contained in:
@@ -111,6 +111,23 @@ public sealed class LaunchProfileOptionsProviderTests
|
||||
Assert.DoesNotContain(hotkeys, hotkey => hotkey.ProfileName == "english");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetProfilesReturnsDefaultAndConfiguredNamedProfiles()
|
||||
{
|
||||
var provider = CreateProvider(new Dictionary<string, string?>
|
||||
{
|
||||
["MeetingAssistant:Hotkey:Toggle"] = "Ctrl+Alt+M",
|
||||
["MeetingAssistant:Recording:TranscriptionProvider"] = "azure-speech",
|
||||
["MeetingAssistant:LaunchProfiles:english:AzureSpeech:Language"] = "en-US"
|
||||
});
|
||||
|
||||
var profiles = provider.GetProfiles();
|
||||
|
||||
Assert.Equal(["default", "english"], profiles.Select(profile => profile.Name));
|
||||
Assert.Equal("azure-speech", profiles.Single(profile => profile.Name == "english").Options.Recording.TranscriptionProvider);
|
||||
Assert.Equal("en-US", profiles.Single(profile => profile.Name == "english").Options.AzureSpeech.Language);
|
||||
}
|
||||
|
||||
private static ConfigurationLaunchProfileOptionsProvider CreateProvider(
|
||||
IReadOnlyDictionary<string, string?> values)
|
||||
{
|
||||
|
||||
@@ -31,6 +31,9 @@ public sealed class MeetingSummaryInstructionBuilderTests
|
||||
Assert.Contains("merge=true", instructions);
|
||||
Assert.Contains("delete_identity", instructions);
|
||||
Assert.Contains("wrongfully matched", instructions);
|
||||
Assert.Contains("oneliner", instructions);
|
||||
Assert.Contains("very short", instructions);
|
||||
Assert.Contains("conciseness is more important", instructions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -48,12 +48,13 @@ public sealed class MeetingSummaryToolTests
|
||||
Assert.Equal("", await tools.ReadGlossary());
|
||||
|
||||
Assert.False(tools.SummaryWasWritten);
|
||||
var result = await tools.WriteSummary("# Summary\n\n- Done.");
|
||||
var result = await tools.WriteSummary("# Summary\n\n- Done.", "Done items");
|
||||
|
||||
Assert.Equal(artifacts.SummaryPath, result);
|
||||
Assert.True(tools.SummaryWasWritten);
|
||||
var summary = await File.ReadAllTextAsync(artifacts.SummaryPath);
|
||||
Assert.Contains("title: Meeting", summary);
|
||||
Assert.Contains("oneliner: Done items", summary);
|
||||
Assert.Contains("start_time: \"2026-05-20T10:00:00.0000000+02:00\"", summary);
|
||||
Assert.Contains("end_time: \"2026-05-20T10:30:00.0000000+02:00\"", summary);
|
||||
Assert.Contains("attendees:", summary);
|
||||
@@ -101,7 +102,7 @@ public sealed class MeetingSummaryToolTests
|
||||
""");
|
||||
var tools = new MeetingSummaryTools(artifacts);
|
||||
|
||||
await tools.WriteSummary("# Summary");
|
||||
await tools.WriteSummary("# Summary", "Preserved attendee summary");
|
||||
|
||||
var summary = await File.ReadAllTextAsync(artifacts.SummaryPath);
|
||||
Assert.Contains("attendees:", summary);
|
||||
@@ -144,7 +145,7 @@ public sealed class MeetingSummaryToolTests
|
||||
""");
|
||||
var tools = new MeetingSummaryTools(artifacts);
|
||||
|
||||
await tools.WriteSummary("# Summary");
|
||||
await tools.WriteSummary("# Summary", "Current project summary");
|
||||
|
||||
var summary = await File.ReadAllTextAsync(artifacts.SummaryPath);
|
||||
Assert.Contains("projects:", summary);
|
||||
@@ -612,7 +613,7 @@ public sealed class MeetingSummaryToolTests
|
||||
audit);
|
||||
|
||||
await tools.WriteContext("owned context");
|
||||
await tools.WriteSummary("owned summary");
|
||||
await tools.WriteSummary("owned summary", "Owned summary");
|
||||
await tools.WriteProjectFile("MeetingAssistant", "notes.md", "TWO", from: 2, to: 2);
|
||||
await tools.AddDictationWord("PBI");
|
||||
await audit.AppendToAssistantContextAsync(CancellationToken.None);
|
||||
@@ -627,4 +628,30 @@ public sealed class MeetingSummaryToolTests
|
||||
Assert.DoesNotContain("owned summary", context);
|
||||
Assert.DoesNotContain("- Context before.", context);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteSummaryRefusesOneLinerWithLineBreaks()
|
||||
{
|
||||
var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
|
||||
var artifacts = new MeetingSessionArtifacts(
|
||||
MeetingNotePath: Path.Combine(root, "Meetings", "Notes", "meeting.md"),
|
||||
TranscriptPath: Path.Combine(root, "Meetings", "Transcripts", "transcript.md"),
|
||||
AssistantContextPath: Path.Combine(root, "Meetings", "Assistant Context", "context.md"),
|
||||
SummaryPath: Path.Combine(root, "Meetings", "Summaries", "summary.md"));
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(artifacts.MeetingNotePath)!);
|
||||
await File.WriteAllTextAsync(
|
||||
artifacts.MeetingNotePath,
|
||||
"""
|
||||
---
|
||||
title: Meeting
|
||||
---
|
||||
""");
|
||||
var tools = new MeetingSummaryTools(artifacts);
|
||||
|
||||
var result = await tools.WriteSummary("# Summary", "First line\nsecond line");
|
||||
|
||||
Assert.Equal("Refused: oneliner must be a single line.", result);
|
||||
Assert.False(tools.SummaryWasWritten);
|
||||
Assert.False(File.Exists(artifacts.SummaryPath));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
using MeetingAssistant.LaunchProfiles;
|
||||
using MeetingAssistant.Recording;
|
||||
using MeetingAssistant.Taskbar;
|
||||
|
||||
namespace MeetingAssistant.Tests;
|
||||
|
||||
public sealed class TaskbarIconTests
|
||||
{
|
||||
[Fact]
|
||||
public void IdleMenuOffersStartForEachConfiguredProfile()
|
||||
{
|
||||
var menu = MeetingTaskbarMenuBuilder.Build(
|
||||
Status(),
|
||||
[Profile("default"), Profile("english")]);
|
||||
|
||||
Assert.Equal(RecordingProcessState.Idle, 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 RecordingMenuOffersStopAbortAndOtherProfileSwitches()
|
||||
{
|
||||
var menu = MeetingTaskbarMenuBuilder.Build(
|
||||
Status(isRecording: true, state: RecordingProcessState.Recording, profile: "default"),
|
||||
[Profile("default"), Profile("english"), Profile("french")]);
|
||||
|
||||
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");
|
||||
Assert.Contains(menu.Items, item =>
|
||||
item.Action == MeetingTaskbarAction.SwitchProfile &&
|
||||
item.ProfileName == "french");
|
||||
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);
|
||||
}
|
||||
|
||||
private static LaunchProfile Profile(string name)
|
||||
{
|
||||
return new LaunchProfile(name, new MeetingAssistantOptions());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user