feat(mission-control): sidebar shows queued+running, no auto-monitor seeding
- TaskStarted no longer creates a monitor pane; Panes stays empty unless a ConPTY session is explicitly opened - SeedActive no longer called on construction or ConnectionRestored - EnsureMonitor / SeedActive kept compiling and functional (internal), just unused - RefreshQueueAsync now loads Queued + Running tasks, Running sorted first - QueuedTaskViewModel gains IsRunning + OpenInAppCommand (IRelayCommand) - Sidebar row is a Button; running rows get RunningTint overlay + "Running" label - HasQueued is true whenever any queued or running task exists - New localization keys: missionControl.running (en + de) - Tests updated: 12 monitor-machinery tests use EnsureMonitor directly; 3 new acceptance-criteria tests (no-auto-pane, running-first sort, row click)
This commit is contained in:
@@ -277,6 +277,7 @@
|
|||||||
"settings": "Einstellungen",
|
"settings": "Einstellungen",
|
||||||
"queue": "Warteschlange",
|
"queue": "Warteschlange",
|
||||||
"blocked": "Blockiert",
|
"blocked": "Blockiert",
|
||||||
|
"running": "Läuft",
|
||||||
"focusMode": "Fokus",
|
"focusMode": "Fokus",
|
||||||
"overviewMode": "Übersicht",
|
"overviewMode": "Übersicht",
|
||||||
"closeSession": "Sitzung schließen",
|
"closeSession": "Sitzung schließen",
|
||||||
|
|||||||
@@ -277,6 +277,7 @@
|
|||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"queue": "Queue",
|
"queue": "Queue",
|
||||||
"blocked": "Blocked",
|
"blocked": "Blocked",
|
||||||
|
"running": "Running",
|
||||||
"focusMode": "Focus",
|
"focusMode": "Focus",
|
||||||
"overviewMode": "Overview",
|
"overviewMode": "Overview",
|
||||||
"closeSession": "Close session",
|
"closeSession": "Close session",
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
|||||||
ConPtySessions.CollectionChanged += OnConPtySessionsChanged;
|
ConPtySessions.CollectionChanged += OnConPtySessionsChanged;
|
||||||
Panes.CollectionChanged += OnPanesChanged;
|
Panes.CollectionChanged += OnPanesChanged;
|
||||||
|
|
||||||
_onTaskStarted = (slot, taskId, startedAt) => { EnsureMonitor(taskId); _ = RefreshQueueAsync(); };
|
_onTaskStarted = (slot, taskId, startedAt) => { _ = RefreshQueueAsync(); };
|
||||||
_worker.TaskStartedEvent += _onTaskStarted;
|
_worker.TaskStartedEvent += _onTaskStarted;
|
||||||
|
|
||||||
_onTaskFinished = (slot, taskId, status, finishedAt) => _ = RefreshQueueAsync();
|
_onTaskFinished = (slot, taskId, status, finishedAt) => _ = RefreshQueueAsync();
|
||||||
@@ -90,10 +90,9 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
|||||||
_onTaskUpdated = taskId => _ = RefreshQueueAsync();
|
_onTaskUpdated = taskId => _ = RefreshQueueAsync();
|
||||||
_worker.TaskUpdatedEvent += _onTaskUpdated;
|
_worker.TaskUpdatedEvent += _onTaskUpdated;
|
||||||
|
|
||||||
_onConnectionRestored = () => { SeedActive(); _ = RefreshQueueAsync(); };
|
_onConnectionRestored = () => { _ = RefreshQueueAsync(); };
|
||||||
_worker.ConnectionRestoredEvent += _onConnectionRestored;
|
_worker.ConnectionRestoredEvent += _onConnectionRestored;
|
||||||
|
|
||||||
SeedActive();
|
|
||||||
_ = RefreshQueueAsync();
|
_ = RefreshQueueAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,19 +102,26 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
|||||||
{
|
{
|
||||||
await using var ctx = await _dbFactory.CreateDbContextAsync();
|
await using var ctx = await _dbFactory.CreateDbContextAsync();
|
||||||
var rows = await ctx.Tasks.AsNoTracking()
|
var rows = await ctx.Tasks.AsNoTracking()
|
||||||
.Where(t => t.Status == ClaudeDo.Data.Models.TaskStatus.Queued)
|
.Where(t => t.Status == ClaudeDo.Data.Models.TaskStatus.Queued
|
||||||
.OrderBy(t => t.SortOrder).ThenBy(t => t.CreatedAt)
|
|| t.Status == ClaudeDo.Data.Models.TaskStatus.Running)
|
||||||
.Select(t => new { t.Id, t.Title, t.BlockedByTaskId })
|
.OrderBy(t => t.Status == ClaudeDo.Data.Models.TaskStatus.Running ? 0 : 1)
|
||||||
|
.ThenBy(t => t.SortOrder).ThenBy(t => t.CreatedAt)
|
||||||
|
.Select(t => new { t.Id, t.Title, t.BlockedByTaskId, t.Status })
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
Queued.Clear();
|
Queued.Clear();
|
||||||
foreach (var r in rows)
|
foreach (var r in rows)
|
||||||
|
{
|
||||||
|
var id = r.Id;
|
||||||
Queued.Add(new QueuedTaskViewModel
|
Queued.Add(new QueuedTaskViewModel
|
||||||
{
|
{
|
||||||
Id = r.Id,
|
Id = id,
|
||||||
Title = r.Title ?? string.Empty,
|
Title = r.Title ?? string.Empty,
|
||||||
IsBlocked = r.BlockedByTaskId != null,
|
IsBlocked = r.BlockedByTaskId != null,
|
||||||
|
IsRunning = r.Status == ClaudeDo.Data.Models.TaskStatus.Running,
|
||||||
|
OpenInAppCommand = new RelayCommand(() => _openInApp?.Invoke(id)),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
OnPropertyChanged(nameof(HasQueued));
|
OnPropertyChanged(nameof(HasQueued));
|
||||||
}
|
}
|
||||||
catch { /* best-effort queue refresh */ }
|
catch { /* best-effort queue refresh */ }
|
||||||
@@ -141,13 +147,15 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
|||||||
await RefreshQueueAsync();
|
await RefreshQueueAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SeedActive()
|
// Kept intentionally — unused since auto-seeding was disabled (2026-07-29).
|
||||||
|
internal void SeedActive()
|
||||||
{
|
{
|
||||||
foreach (var a in _worker.GetActiveTasks())
|
foreach (var a in _worker.GetActiveTasks())
|
||||||
EnsureMonitor(a.TaskId);
|
EnsureMonitor(a.TaskId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureMonitor(string taskId)
|
// Kept intentionally — unused since auto-seeding was disabled (2026-07-29).
|
||||||
|
internal void EnsureMonitor(string taskId)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(taskId)) return;
|
if (string.IsNullOrEmpty(taskId)) return;
|
||||||
if (Monitors.Any(m => m.SubscribedTaskId == taskId)) return;
|
if (Monitors.Any(m => m.SubscribedTaskId == taskId)) return;
|
||||||
@@ -428,10 +436,12 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Read-only display row for a queued task in the Mission Control side strip.</summary>
|
/// <summary>Read-only display row for a queued or running task in the Mission Control side strip.</summary>
|
||||||
public sealed class QueuedTaskViewModel
|
public sealed class QueuedTaskViewModel
|
||||||
{
|
{
|
||||||
public required string Id { get; init; }
|
public required string Id { get; init; }
|
||||||
public required string Title { get; init; }
|
public required string Title { get; init; }
|
||||||
public bool IsBlocked { get; init; }
|
public bool IsBlocked { get; init; }
|
||||||
|
public bool IsRunning { get; init; }
|
||||||
|
public IRelayCommand? OpenInAppCommand { get; init; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,11 +73,20 @@
|
|||||||
<ItemsControl ItemsSource="{Binding Queued}">
|
<ItemsControl ItemsSource="{Binding Queued}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:QueuedTaskViewModel">
|
<DataTemplate x:DataType="vm:QueuedTaskViewModel">
|
||||||
<Border Margin="0,0,0,4" Padding="8,6"
|
<Button Command="{Binding OpenInAppCommand}"
|
||||||
Background="{DynamicResource SurfaceBrush}"
|
Padding="0" Margin="0,0,0,4"
|
||||||
|
Background="Transparent" BorderThickness="0"
|
||||||
|
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||||
|
Cursor="Hand">
|
||||||
|
<Panel>
|
||||||
|
<Border Background="{DynamicResource SurfaceBrush}"
|
||||||
BorderBrush="{DynamicResource LineBrush}"
|
BorderBrush="{DynamicResource LineBrush}"
|
||||||
BorderThickness="1" CornerRadius="6">
|
BorderThickness="1" CornerRadius="6" />
|
||||||
<StackPanel Spacing="2">
|
<Border Background="{DynamicResource RunningTintBrush}"
|
||||||
|
BorderBrush="{DynamicResource RunningTintBorderBrush}"
|
||||||
|
BorderThickness="1" CornerRadius="6"
|
||||||
|
IsVisible="{Binding IsRunning}" />
|
||||||
|
<StackPanel Margin="8,6" Spacing="2">
|
||||||
<TextBlock Text="{Binding Title}"
|
<TextBlock Text="{Binding Title}"
|
||||||
TextTrimming="CharacterEllipsis"
|
TextTrimming="CharacterEllipsis"
|
||||||
ToolTip.Tip="{Binding Title}"
|
ToolTip.Tip="{Binding Title}"
|
||||||
@@ -86,8 +95,13 @@
|
|||||||
Text="{loc:Tr missionControl.blocked}"
|
Text="{loc:Tr missionControl.blocked}"
|
||||||
IsVisible="{Binding IsBlocked}"
|
IsVisible="{Binding IsBlocked}"
|
||||||
Foreground="{DynamicResource AmberBrush}" />
|
Foreground="{DynamicResource AmberBrush}" />
|
||||||
|
<TextBlock Classes="meta"
|
||||||
|
Text="{loc:Tr missionControl.running}"
|
||||||
|
IsVisible="{Binding IsRunning}"
|
||||||
|
Foreground="{DynamicResource StatusRunningBrush}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Panel>
|
||||||
|
</Button>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|||||||
@@ -49,8 +49,10 @@ public class MissionControlViewModelTests : IDisposable
|
|||||||
private MissionControlViewModel BuildVm(StubWorkerClient worker)
|
private MissionControlViewModel BuildVm(StubWorkerClient worker)
|
||||||
=> new MissionControlViewModel(new TestDbFactory(NewContext), worker);
|
=> new MissionControlViewModel(new TestDbFactory(NewContext), worker);
|
||||||
|
|
||||||
|
// ── acceptance criterion (a): TaskStarted must NOT add a pane ──────────────
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TwoStarts_CreateTwoMonitors_ColumnCountTwo()
|
public void TaskStarted_DoesNotAddPane()
|
||||||
{
|
{
|
||||||
var worker = new FakeWorker();
|
var worker = new FakeWorker();
|
||||||
using var vm = BuildVm(worker);
|
using var vm = BuildVm(worker);
|
||||||
@@ -58,18 +60,140 @@ public class MissionControlViewModelTests : IDisposable
|
|||||||
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
|
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
|
||||||
worker.RaiseTaskStarted("slot-2", "t2", DateTime.UtcNow);
|
worker.RaiseTaskStarted("slot-2", "t2", DateTime.UtcNow);
|
||||||
|
|
||||||
|
Assert.Empty(vm.Monitors);
|
||||||
|
Assert.Empty(vm.Panes);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Construction_DoesNotSeedMonitorsEvenWithActiveTasks()
|
||||||
|
{
|
||||||
|
var worker = new SeededFakeWorker();
|
||||||
|
using var vm = BuildVm(worker);
|
||||||
|
|
||||||
|
Assert.Empty(vm.Monitors);
|
||||||
|
Assert.Empty(vm.Panes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class SeededFakeWorker : StubWorkerClient
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<ActiveTask> GetActiveTasks()
|
||||||
|
=> new[] { new ActiveTask("slot-1", "seed1", DateTime.UtcNow) };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── acceptance criterion (b): RefreshQueueAsync returns Running rows first ─
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RefreshQueueAsync_RunningFirst_WithIsRunningSet()
|
||||||
|
{
|
||||||
|
await using (var db = NewContext())
|
||||||
|
{
|
||||||
|
db.Lists.Add(new ListEntity { Id = "L1", Name = "Work", CreatedAt = DateTime.UtcNow });
|
||||||
|
// Running task has higher SortOrder — must still appear first
|
||||||
|
db.Tasks.Add(new TaskEntity { Id = "r1", ListId = "L1", Title = "running-task", Status = TaskStatus.Running, CreatedAt = DateTime.UtcNow, SortOrder = 10 });
|
||||||
|
db.Tasks.Add(new TaskEntity { Id = "q1", ListId = "L1", Title = "queued-task", Status = TaskStatus.Queued, CreatedAt = DateTime.UtcNow, SortOrder = 0 });
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var worker = new FakeWorker();
|
||||||
|
using var vm = BuildVm(worker);
|
||||||
|
|
||||||
|
await vm.RefreshQueueAsync();
|
||||||
|
|
||||||
|
Assert.True(vm.HasQueued);
|
||||||
|
Assert.Equal(2, vm.Queued.Count);
|
||||||
|
|
||||||
|
Assert.Equal("r1", vm.Queued[0].Id);
|
||||||
|
Assert.True(vm.Queued[0].IsRunning);
|
||||||
|
|
||||||
|
Assert.Equal("q1", vm.Queued[1].Id);
|
||||||
|
Assert.False(vm.Queued[1].IsRunning);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task HasQueued_TrueWhenOnlyRunningTasksExist()
|
||||||
|
{
|
||||||
|
await using (var db = NewContext())
|
||||||
|
{
|
||||||
|
db.Lists.Add(new ListEntity { Id = "L1", Name = "Work", CreatedAt = DateTime.UtcNow });
|
||||||
|
db.Tasks.Add(new TaskEntity { Id = "r1", ListId = "L1", Title = "running", Status = TaskStatus.Running, CreatedAt = DateTime.UtcNow, SortOrder = 0 });
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var worker = new FakeWorker();
|
||||||
|
using var vm = BuildVm(worker);
|
||||||
|
|
||||||
|
await vm.RefreshQueueAsync();
|
||||||
|
|
||||||
|
Assert.True(vm.HasQueued);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── acceptance criterion (c): row click invokes OpenInApp ──────────────────
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Row_Click_InvokesOpenInApp()
|
||||||
|
{
|
||||||
|
await using (var db = NewContext())
|
||||||
|
{
|
||||||
|
db.Lists.Add(new ListEntity { Id = "L1", Name = "Work", CreatedAt = DateTime.UtcNow });
|
||||||
|
db.Tasks.Add(new TaskEntity { Id = "q1", ListId = "L1", Title = "clickable", Status = TaskStatus.Queued, CreatedAt = DateTime.UtcNow, SortOrder = 0 });
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var worker = new FakeWorker();
|
||||||
|
using var vm = BuildVm(worker);
|
||||||
|
string? opened = null;
|
||||||
|
vm.OpenInApp = id => opened = id;
|
||||||
|
|
||||||
|
await vm.RefreshQueueAsync();
|
||||||
|
vm.Queued[0].OpenInAppCommand!.Execute(null);
|
||||||
|
|
||||||
|
Assert.Equal("q1", opened);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Row_Click_RunningTask_InvokesOpenInApp()
|
||||||
|
{
|
||||||
|
await using (var db = NewContext())
|
||||||
|
{
|
||||||
|
db.Lists.Add(new ListEntity { Id = "L1", Name = "Work", CreatedAt = DateTime.UtcNow });
|
||||||
|
db.Tasks.Add(new TaskEntity { Id = "r1", ListId = "L1", Title = "running", Status = TaskStatus.Running, CreatedAt = DateTime.UtcNow, SortOrder = 0 });
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var worker = new FakeWorker();
|
||||||
|
using var vm = BuildVm(worker);
|
||||||
|
string? opened = null;
|
||||||
|
vm.OpenInApp = id => opened = id;
|
||||||
|
|
||||||
|
await vm.RefreshQueueAsync();
|
||||||
|
vm.Queued[0].OpenInAppCommand!.Execute(null);
|
||||||
|
|
||||||
|
Assert.Equal("r1", opened);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── monitor machinery tests (EnsureMonitor still works when called directly) ─
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnsureMonitor_TwoCalls_CreateTwoMonitors_ColumnCountTwo()
|
||||||
|
{
|
||||||
|
var worker = new FakeWorker();
|
||||||
|
using var vm = BuildVm(worker);
|
||||||
|
|
||||||
|
vm.EnsureMonitor("t1");
|
||||||
|
vm.EnsureMonitor("t2");
|
||||||
|
|
||||||
Assert.Equal(2, vm.Monitors.Count);
|
Assert.Equal(2, vm.Monitors.Count);
|
||||||
Assert.Equal(2, vm.ColumnCount);
|
Assert.Equal(2, vm.ColumnCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DuplicateStart_DoesNotAddSecondMonitor()
|
public void EnsureMonitor_DuplicateCall_DoesNotAddSecondMonitor()
|
||||||
{
|
{
|
||||||
var worker = new FakeWorker();
|
var worker = new FakeWorker();
|
||||||
using var vm = BuildVm(worker);
|
using var vm = BuildVm(worker);
|
||||||
|
|
||||||
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
|
vm.EnsureMonitor("t1");
|
||||||
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
|
vm.EnsureMonitor("t1");
|
||||||
|
|
||||||
Assert.Equal(1, vm.Monitors.Count);
|
Assert.Equal(1, vm.Monitors.Count);
|
||||||
}
|
}
|
||||||
@@ -80,7 +204,7 @@ public class MissionControlViewModelTests : IDisposable
|
|||||||
var worker = new FakeWorker();
|
var worker = new FakeWorker();
|
||||||
using var vm = BuildVm(worker);
|
using var vm = BuildVm(worker);
|
||||||
|
|
||||||
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
|
vm.EnsureMonitor("t1");
|
||||||
worker.RaiseTaskFinished("slot-1", "t1", "done", DateTime.UtcNow);
|
worker.RaiseTaskFinished("slot-1", "t1", "done", DateTime.UtcNow);
|
||||||
|
|
||||||
Assert.Equal(1, vm.Monitors.Count);
|
Assert.Equal(1, vm.Monitors.Count);
|
||||||
@@ -93,8 +217,8 @@ public class MissionControlViewModelTests : IDisposable
|
|||||||
var worker = new FakeWorker();
|
var worker = new FakeWorker();
|
||||||
using var vm = BuildVm(worker);
|
using var vm = BuildVm(worker);
|
||||||
|
|
||||||
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
|
vm.EnsureMonitor("t1");
|
||||||
worker.RaiseTaskStarted("slot-2", "t2", DateTime.UtcNow);
|
vm.EnsureMonitor("t2");
|
||||||
worker.RaiseTaskFinished("slot-1", "t1", "done", DateTime.UtcNow);
|
worker.RaiseTaskFinished("slot-1", "t1", "done", DateTime.UtcNow);
|
||||||
|
|
||||||
vm.ClearFinishedCommand.Execute(null);
|
vm.ClearFinishedCommand.Execute(null);
|
||||||
@@ -104,22 +228,6 @@ public class MissionControlViewModelTests : IDisposable
|
|||||||
Assert.Equal(1, vm.ColumnCount);
|
Assert.Equal(1, vm.ColumnCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void SeedsFromActiveTasksOnConstruction()
|
|
||||||
{
|
|
||||||
var worker = new SeededFakeWorker();
|
|
||||||
using var vm = BuildVm(worker);
|
|
||||||
|
|
||||||
Assert.Equal(1, vm.Monitors.Count);
|
|
||||||
Assert.Equal("seed1", vm.Monitors[0].SubscribedTaskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class SeededFakeWorker : StubWorkerClient
|
|
||||||
{
|
|
||||||
public override IReadOnlyList<ActiveTask> GetActiveTasks()
|
|
||||||
=> new[] { new ActiveTask("slot-1", "seed1", DateTime.UtcNow) };
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OpenInApp_PropagatesToMonitors_AndCommandInvokesHook()
|
public void OpenInApp_PropagatesToMonitors_AndCommandInvokesHook()
|
||||||
{
|
{
|
||||||
@@ -129,7 +237,7 @@ public class MissionControlViewModelTests : IDisposable
|
|||||||
string? revealed = null;
|
string? revealed = null;
|
||||||
vm.OpenInApp = id => revealed = id;
|
vm.OpenInApp = id => revealed = id;
|
||||||
|
|
||||||
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
|
vm.EnsureMonitor("t1");
|
||||||
|
|
||||||
vm.Monitors[0].OpenInAppCommand.Execute(null);
|
vm.Monitors[0].OpenInAppCommand.Execute(null);
|
||||||
|
|
||||||
@@ -145,7 +253,7 @@ public class MissionControlViewModelTests : IDisposable
|
|||||||
Action? reDock = null;
|
Action? reDock = null;
|
||||||
vm.ShowDetached = (m, rd) => { detached = m; reDock = rd; };
|
vm.ShowDetached = (m, rd) => { detached = m; reDock = rd; };
|
||||||
|
|
||||||
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
|
vm.EnsureMonitor("t1");
|
||||||
var monitor = vm.Monitors[0];
|
var monitor = vm.Monitors[0];
|
||||||
|
|
||||||
monitor.DetachCommand.Execute(null);
|
monitor.DetachCommand.Execute(null);
|
||||||
@@ -162,7 +270,7 @@ public class MissionControlViewModelTests : IDisposable
|
|||||||
{
|
{
|
||||||
var worker = new FakeWorker();
|
var worker = new FakeWorker();
|
||||||
using var vm = BuildVm(worker);
|
using var vm = BuildVm(worker);
|
||||||
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
|
vm.EnsureMonitor("t1");
|
||||||
worker.RaiseTaskFinished("slot-1", "t1", "waiting_for_review", DateTime.UtcNow);
|
worker.RaiseTaskFinished("slot-1", "t1", "waiting_for_review", DateTime.UtcNow);
|
||||||
|
|
||||||
Assert.True(vm.Monitors[0].IsWaitingForReview);
|
Assert.True(vm.Monitors[0].IsWaitingForReview);
|
||||||
@@ -177,7 +285,7 @@ public class MissionControlViewModelTests : IDisposable
|
|||||||
using var vm = BuildVm(worker);
|
using var vm = BuildVm(worker);
|
||||||
Action? reDock = null;
|
Action? reDock = null;
|
||||||
vm.ShowDetached = (m, rd) => reDock = rd;
|
vm.ShowDetached = (m, rd) => reDock = rd;
|
||||||
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
|
vm.EnsureMonitor("t1");
|
||||||
var monitor = vm.Monitors[0];
|
var monitor = vm.Monitors[0];
|
||||||
|
|
||||||
monitor.DetachCommand.Execute(null);
|
monitor.DetachCommand.Execute(null);
|
||||||
@@ -195,7 +303,7 @@ public class MissionControlViewModelTests : IDisposable
|
|||||||
var worker = new FakeWorker();
|
var worker = new FakeWorker();
|
||||||
using var vm = BuildVm(worker);
|
using var vm = BuildVm(worker);
|
||||||
vm.ShowDetached = (m, rd) => { };
|
vm.ShowDetached = (m, rd) => { };
|
||||||
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
|
vm.EnsureMonitor("t1");
|
||||||
var monitor = vm.Monitors[0];
|
var monitor = vm.Monitors[0];
|
||||||
|
|
||||||
var closeRequested = false;
|
var closeRequested = false;
|
||||||
@@ -210,9 +318,9 @@ public class MissionControlViewModelTests : IDisposable
|
|||||||
{
|
{
|
||||||
var worker = new FakeWorker();
|
var worker = new FakeWorker();
|
||||||
using var vm = BuildVm(worker);
|
using var vm = BuildVm(worker);
|
||||||
worker.RaiseTaskStarted("s1", "t1", DateTime.UtcNow);
|
vm.EnsureMonitor("t1");
|
||||||
worker.RaiseTaskStarted("s2", "t2", DateTime.UtcNow);
|
vm.EnsureMonitor("t2");
|
||||||
worker.RaiseTaskStarted("s3", "t3", DateTime.UtcNow);
|
vm.EnsureMonitor("t3");
|
||||||
|
|
||||||
vm.MoveMonitor(vm.Monitors[0], vm.Monitors[2]); // move t1 to t3's slot
|
vm.MoveMonitor(vm.Monitors[0], vm.Monitors[2]); // move t1 to t3's slot
|
||||||
Assert.Equal(new[] { "t2", "t3", "t1" }, vm.Monitors.Select(m => m.SubscribedTaskId).ToArray());
|
Assert.Equal(new[] { "t2", "t3", "t1" }, vm.Monitors.Select(m => m.SubscribedTaskId).ToArray());
|
||||||
@@ -320,7 +428,7 @@ public class MissionControlViewModelTests : IDisposable
|
|||||||
var worker = new FakeWorker();
|
var worker = new FakeWorker();
|
||||||
using var vm = BuildVm(worker);
|
using var vm = BuildVm(worker);
|
||||||
|
|
||||||
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
|
vm.EnsureMonitor("t1");
|
||||||
await vm.OpenConPtySessionAsync("t2");
|
await vm.OpenConPtySessionAsync("t2");
|
||||||
|
|
||||||
Assert.Equal(2, vm.Panes.Count);
|
Assert.Equal(2, vm.Panes.Count);
|
||||||
|
|||||||
Reference in New Issue
Block a user