From aa42e8edda69253af50f5e21ec2766a75ad111f5 Mon Sep 17 00:00:00 2001 From: Codex Automation Date: Tue, 4 Aug 2026 10:11:43 +0200 Subject: [PATCH] refactor: tighten tray menu section handling --- MeetingAssistant.Tests/TaskbarIconTests.cs | 103 +++++++++--------- .../Taskbar/MeetingTaskbarMenu.cs | 6 +- .../tasks.md | 6 + 3 files changed, 59 insertions(+), 56 deletions(-) diff --git a/MeetingAssistant.Tests/TaskbarIconTests.cs b/MeetingAssistant.Tests/TaskbarIconTests.cs index 5eaed5d..b1cdbb6 100644 --- a/MeetingAssistant.Tests/TaskbarIconTests.cs +++ b/MeetingAssistant.Tests/TaskbarIconTests.cs @@ -13,22 +13,23 @@ public sealed class TaskbarIconTests { var menu = MeetingTaskbarMenuBuilder.Build( Status(), - [Profile("default", "Ctrl+Alt+M"), Profile("english", "Ctrl+Alt+L")]); + [Profile("default", "Ctrl+Alt+M"), Profile("english", "Ctrl+Alt+L")], + [new MicrophoneDevice("integrated", "integrated microphone")], + "integrated"); Assert.Equal(RecordingProcessState.Idle, menu.State); - Assert.Contains(menu.Items, item => - item.Action == MeetingTaskbarAction.EditRules && - item.Text == "Open agent"); - 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); + AssertMenuLayout( + menu, + ("Open agent", MeetingTaskbarAction.EditRules, false), + ("Microphone", MeetingTaskbarAction.OpenSubmenu, true), + ("Start meeting recording (default)\tCtrl+Alt+M", MeetingTaskbarAction.StartRecording, false), + ("Start meeting recording (english)\tCtrl+Alt+L", MeetingTaskbarAction.StartRecording, false), + ("Exit", MeetingTaskbarAction.Exit, true)); + Assert.Equal( + ["default", "english"], + menu.Items + .Where(item => item.Action == MeetingTaskbarAction.StartRecording) + .Select(item => item.ProfileName)); } [Fact] @@ -69,45 +70,32 @@ public sealed class TaskbarIconTests "integrated"); Assert.Equal(RecordingProcessState.Recording, menu.State); - Assert.Collection( - menu.Items, - item => - { - Assert.Equal("Open agent", item.Text); - Assert.Equal(MeetingTaskbarAction.EditRules, item.Action); - Assert.False(item.StartsSection); - }, - item => - { - Assert.Equal("Finish meeting", item.Text); - Assert.Equal(MeetingTaskbarAction.StopRecording, item.Action); - Assert.True(item.StartsSection); - }, - item => - { - Assert.Equal("Microphone", item.Text); - Assert.Equal(MeetingTaskbarAction.OpenSubmenu, item.Action); - Assert.True(item.StartsSection); - }, - item => - { - Assert.Equal("Cancel meeting recording and discard", item.Text); - Assert.Equal(MeetingTaskbarAction.AbortRecording, item.Action); - Assert.False(item.StartsSection); - }, - item => - { - Assert.Equal("Switch to english\tCtrl+Alt+L", item.Text); - Assert.Equal(MeetingTaskbarAction.SwitchProfile, item.Action); - Assert.Equal("english", item.ProfileName); - Assert.False(item.StartsSection); - }, - item => - { - Assert.Equal("Exit", item.Text); - Assert.Equal(MeetingTaskbarAction.Exit, item.Action); - Assert.True(item.StartsSection); - }); + AssertMenuLayout( + menu, + ("Open agent", MeetingTaskbarAction.EditRules, false), + ("Finish meeting", MeetingTaskbarAction.StopRecording, true), + ("Microphone", MeetingTaskbarAction.OpenSubmenu, true), + ("Cancel meeting recording and discard", MeetingTaskbarAction.AbortRecording, false), + ("Switch to english\tCtrl+Alt+L", MeetingTaskbarAction.SwitchProfile, false), + ("Exit", MeetingTaskbarAction.Exit, true)); + Assert.Equal( + "english", + Assert.Single(menu.Items, item => item.Action == MeetingTaskbarAction.SwitchProfile).ProfileName); + } + + [Fact] + public void RecordingMenuKeepsFinishMeetingIsolatedWithoutMicrophones() + { + var menu = MeetingTaskbarMenuBuilder.Build( + Status(isRecording: true, state: RecordingProcessState.Recording, profile: "default"), + [Profile("default")]); + + AssertMenuLayout( + menu, + ("Open agent", MeetingTaskbarAction.EditRules, false), + ("Finish meeting", MeetingTaskbarAction.StopRecording, true), + ("Cancel meeting recording and discard", MeetingTaskbarAction.AbortRecording, true), + ("Exit", MeetingTaskbarAction.Exit, true)); } [Fact] @@ -200,6 +188,15 @@ public sealed class TaskbarIconTests }); } + private static void AssertMenuLayout( + MeetingTaskbarMenu menu, + params (string Text, MeetingTaskbarAction Action, bool StartsSection)[] expected) + { + Assert.Equal( + expected, + menu.Items.Select(item => (item.Text, item.Action, item.StartsSection))); + } + private static RecordingStatus Status( bool isRecording = false, RecordingProcessState state = RecordingProcessState.Idle, diff --git a/MeetingAssistant/Taskbar/MeetingTaskbarMenu.cs b/MeetingAssistant/Taskbar/MeetingTaskbarMenu.cs index ce3494f..a59a485 100644 --- a/MeetingAssistant/Taskbar/MeetingTaskbarMenu.cs +++ b/MeetingAssistant/Taskbar/MeetingTaskbarMenu.cs @@ -113,15 +113,15 @@ public static class MeetingTaskbarMenuBuilder private static void AddSection( List items, - List section) + IReadOnlyList section) { if (section.Count == 0) { return; } - section[0] = section[0] with { StartsSection = true }; - items.AddRange(section); + items.Add(section[0] with { StartsSection = true }); + items.AddRange(section.Skip(1)); } private static string BuildTooltip(RecordingStatus status) diff --git a/openspec/changes/prioritize-finish-meeting-tray-action/tasks.md b/openspec/changes/prioritize-finish-meeting-tray-action/tasks.md index bbb9ffb..15a6f24 100644 --- a/openspec/changes/prioritize-finish-meeting-tray-action/tasks.md +++ b/openspec/changes/prioritize-finish-meeting-tray-action/tasks.md @@ -7,3 +7,9 @@ - [x] 2.1 Review the touched menu builder and renderer for DRYness, SOLID design, and simplicity while preserving behavior. - [x] 2.2 Run focused taskbar-menu tests, the Windows application build, the full solution tests, and strict OpenSpec validation. + +## 3. Refactor Follow-up + +- [x] 3.1 Lock down idle section boundaries and active-recording layout when no microphone is available. +- [x] 3.2 Remove the tray-menu section helper's hidden input mutation without changing rendered behavior. +- [x] 3.3 Run focused and full verification, then validate the OpenSpec change strictly.