Group 1 of the task-row context menu gets a fourth gated entry, right after "Open interactive session": it opens the task's worktree folder via Process.Start/UseShellExecute, mirroring ListsIslandViewModel's OpenInExplorer. Missing worktree greys the item out with a tooltip reason instead of hiding it, keeping Group 1's fixed length. Failures report through TasksIslandViewModel.ErrorReported (footer strip), not a silent catch.
142 lines
4.3 KiB
C#
142 lines
4.3 KiB
C#
using System.Collections.Generic;
|
|
using ClaudeDo.Data.Models;
|
|
using ClaudeDo.Ui.ViewModels.Islands;
|
|
using Xunit;
|
|
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
|
|
|
namespace ClaudeDo.Worker.Tests.UiVm;
|
|
|
|
public class TaskRowViewModelTests
|
|
{
|
|
[Theory]
|
|
[InlineData(TaskStatus.Running, "running")]
|
|
[InlineData(TaskStatus.WaitingForReview, "review")]
|
|
[InlineData(TaskStatus.Failed, "error")]
|
|
[InlineData(TaskStatus.Done, "done")]
|
|
[InlineData(TaskStatus.Queued, "queued")]
|
|
[InlineData(TaskStatus.Idle, "idle")]
|
|
public void StatusChipClass_Maps_Correctly(TaskStatus s, string expected)
|
|
{
|
|
var vm = new TaskRowViewModel { Id = "t" };
|
|
vm.Status = s;
|
|
Assert.Equal(expected, vm.StatusChipClass);
|
|
}
|
|
|
|
[Fact]
|
|
public void Number_SetFromEntity_SurvivesUpdateFromEntity()
|
|
{
|
|
var entity = new TaskEntity
|
|
{
|
|
Id = "t", ListId = "l1", Title = "Task", Number = 123, CreatedAt = DateTime.UtcNow,
|
|
};
|
|
var vm = TaskRowViewModel.FromEntity(entity);
|
|
Assert.Equal(123, vm.Number);
|
|
|
|
// Simulates the TaskUpdated refresh path: the entity is re-fetched and the same
|
|
// row is updated in place rather than replaced.
|
|
entity.Title = "Task (edited)";
|
|
vm.UpdateFromEntity(entity);
|
|
|
|
Assert.Equal(123, vm.Number);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(-1, false)]
|
|
[InlineData(0, false)]
|
|
[InlineData(1, true)]
|
|
[InlineData(42, true)]
|
|
public void ShowNumberBadge_Hides_For_Zero_And_Negative(int number, bool expected)
|
|
{
|
|
var vm = new TaskRowViewModel { Id = "t" };
|
|
vm.Number = number;
|
|
Assert.Equal(expected, vm.ShowNumberBadge);
|
|
}
|
|
|
|
[Fact]
|
|
public void ShowNumberBadge_Raises_PropertyChanged_On_Number_Change()
|
|
{
|
|
var vm = new TaskRowViewModel { Id = "t" };
|
|
var raised = new List<string?>();
|
|
vm.PropertyChanged += (_, e) => raised.Add(e.PropertyName);
|
|
|
|
vm.Number = 5;
|
|
|
|
Assert.Contains(nameof(TaskRowViewModel.ShowNumberBadge), raised);
|
|
}
|
|
|
|
[Fact]
|
|
public void CanOpenWorktree_True_And_ReasonNull_When_Path_Present()
|
|
{
|
|
var vm = new TaskRowViewModel { Id = "t" };
|
|
vm.WorktreePath = @"C:\repo\worktrees\t";
|
|
|
|
Assert.True(vm.CanOpenWorktree);
|
|
Assert.Null(vm.OpenWorktreeDisabledReason);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData(" ")]
|
|
public void CanOpenWorktree_False_And_ReasonSet_When_Path_Missing(string? path)
|
|
{
|
|
var vm = new TaskRowViewModel { Id = "t" };
|
|
vm.WorktreePath = path;
|
|
|
|
Assert.False(vm.CanOpenWorktree);
|
|
Assert.NotNull(vm.OpenWorktreeDisabledReason);
|
|
}
|
|
|
|
[Fact]
|
|
public void OpenWorktreeGate_Raises_PropertyChanged_On_WorktreePath_Change()
|
|
{
|
|
var vm = new TaskRowViewModel { Id = "t" };
|
|
var raised = new List<string?>();
|
|
vm.PropertyChanged += (_, e) => raised.Add(e.PropertyName);
|
|
|
|
vm.WorktreePath = @"C:\repo\worktrees\t";
|
|
|
|
Assert.Contains(nameof(TaskRowViewModel.CanOpenWorktree), raised);
|
|
Assert.Contains(nameof(TaskRowViewModel.OpenWorktreeDisabledReason), raised);
|
|
}
|
|
|
|
[Fact]
|
|
public void WorktreePath_SetFromEntity_MapsFromWorktreePath()
|
|
{
|
|
var entity = new TaskEntity
|
|
{
|
|
Id = "t", ListId = "l1", Title = "Task", CreatedAt = DateTime.UtcNow,
|
|
Worktree = new WorktreeEntity
|
|
{
|
|
TaskId = "t", Path = @"C:\repo\worktrees\t", BranchName = "b",
|
|
BaseCommit = "abc123", CreatedAt = DateTime.UtcNow,
|
|
},
|
|
};
|
|
var vm = TaskRowViewModel.FromEntity(entity);
|
|
|
|
Assert.Equal(@"C:\repo\worktrees\t", vm.WorktreePath);
|
|
Assert.True(vm.CanOpenWorktree);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsDropTarget_Follows_Either_DropHint_And_Raises_PropertyChanged()
|
|
{
|
|
var vm = new TaskRowViewModel { Id = "t" };
|
|
var raised = new List<string?>();
|
|
vm.PropertyChanged += (_, e) => raised.Add(e.PropertyName);
|
|
|
|
Assert.False(vm.IsDropTarget);
|
|
|
|
vm.DropHintAbove = true;
|
|
Assert.True(vm.IsDropTarget);
|
|
Assert.Contains(nameof(TaskRowViewModel.IsDropTarget), raised);
|
|
|
|
vm.DropHintAbove = false;
|
|
vm.DropHintBelow = true;
|
|
Assert.True(vm.IsDropTarget);
|
|
|
|
vm.DropHintBelow = false;
|
|
Assert.False(vm.IsDropTarget);
|
|
}
|
|
}
|