chore(claude-do): TaskRowViewModel: manuelle OnPropertyChanged-Kette auf [Noti
src/ClaudeDo.Ui/ViewModels/Islands/TaskRowViewModel.cs enthält ~107 handgeschriebene OnPropertyChanged(nameof(...))-Aufrufe in partial void OnXChanged-Methoden. Das Projekt nutzt an anderen Stellen den Generator: [NotifyPropertyChangedFor] in DetailsIslandViewModel.cs, ConflictResolverViewModel.cs, AgentConfigEditorViewModel.cs, Installer/WizardViewModel.cs. Das ist die eine Datei, in der eine ve ClaudeDo-Task: 87976462-5845-4e01-8233-32afadca02bd
This commit is contained in:
@@ -0,0 +1,463 @@
|
||||
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;
|
||||
|
||||
// Characterization test for TaskRowViewModel's PropertyChanged fan-out. For every source
|
||||
// property that today drives dependent notifications (via a handwritten partial OnXChanged),
|
||||
// this pins down the exact SET of names raised when that property changes. Order is
|
||||
// deliberately not asserted: a NotifyPropertyChangedFor-based rewrite raises the source
|
||||
// property's own notification before its dependents instead of after, which is an unobservable
|
||||
// reordering, not a behavior change. This test must stay green across that rewrite.
|
||||
public class TaskRowViewModelNotificationsTests
|
||||
{
|
||||
private static HashSet<string?> Raised(TaskRowViewModel vm, System.Action mutate)
|
||||
{
|
||||
var raised = new HashSet<string?>();
|
||||
vm.PropertyChanged += (_, e) => raised.Add(e.PropertyName);
|
||||
mutate();
|
||||
return raised;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StatusChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.Status = TaskStatus.Running);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.Status),
|
||||
nameof(TaskRowViewModel.StatusChipClass),
|
||||
nameof(TaskRowViewModel.StatusLabel),
|
||||
nameof(TaskRowViewModel.ShowStatusChip),
|
||||
nameof(TaskRowViewModel.IsRunning),
|
||||
nameof(TaskRowViewModel.IsWaitingForReview),
|
||||
nameof(TaskRowViewModel.IsParked),
|
||||
nameof(TaskRowViewModel.IsQueued),
|
||||
nameof(TaskRowViewModel.IsWaiting),
|
||||
nameof(TaskRowViewModel.HasFailureReason),
|
||||
nameof(TaskRowViewModel.FailureReasonTooltip),
|
||||
nameof(TaskRowViewModel.StatusChipTooltip),
|
||||
nameof(TaskRowViewModel.IsDraft),
|
||||
nameof(TaskRowViewModel.IsPlanned),
|
||||
nameof(TaskRowViewModel.CanOpenPlanningSession),
|
||||
nameof(TaskRowViewModel.CanRemoveFromQueue),
|
||||
nameof(TaskRowViewModel.CanSendToQueue),
|
||||
nameof(TaskRowViewModel.CanRefine),
|
||||
nameof(TaskRowViewModel.SendToQueueDisabledReason),
|
||||
nameof(TaskRowViewModel.CancelDisabledReason),
|
||||
nameof(TaskRowViewModel.RefineDisabledReason),
|
||||
nameof(TaskRowViewModel.PlanningDisabledReason),
|
||||
nameof(TaskRowViewModel.MarkAsDisabledReason),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParentTaskIdChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.ParentTaskId = "parent");
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.ParentTaskId),
|
||||
nameof(TaskRowViewModel.IsChild),
|
||||
nameof(TaskRowViewModel.IsAgentSuggested),
|
||||
nameof(TaskRowViewModel.ShowAsChild),
|
||||
nameof(TaskRowViewModel.IsDraft),
|
||||
nameof(TaskRowViewModel.IsPlanned),
|
||||
nameof(TaskRowViewModel.CanSendToQueue),
|
||||
nameof(TaskRowViewModel.CanOpenPlanningSession),
|
||||
nameof(TaskRowViewModel.SendToQueueDisabledReason),
|
||||
nameof(TaskRowViewModel.PlanningDisabledReason),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParentInViewChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.ParentInView = false);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.ParentInView),
|
||||
nameof(TaskRowViewModel.ShowAsChild),
|
||||
nameof(TaskRowViewModel.IsDraft),
|
||||
nameof(TaskRowViewModel.IsPlanned),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreatedByChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.CreatedBy = "someone");
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.CreatedBy),
|
||||
nameof(TaskRowViewModel.IsAgentSuggested),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParentFinalizedChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.ParentFinalized = true);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.ParentFinalized),
|
||||
nameof(TaskRowViewModel.IsDraft),
|
||||
nameof(TaskRowViewModel.IsPlanned),
|
||||
nameof(TaskRowViewModel.CanSendToQueue),
|
||||
nameof(TaskRowViewModel.SendToQueueDisabledReason),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PlanningPhaseChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.PlanningPhase = PlanningPhase.Active);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.PlanningPhase),
|
||||
nameof(TaskRowViewModel.IsPlanningParent),
|
||||
nameof(TaskRowViewModel.PlanningBadge),
|
||||
nameof(TaskRowViewModel.ShowStatusChip),
|
||||
nameof(TaskRowViewModel.IsPlanActive),
|
||||
nameof(TaskRowViewModel.IsPlanFinalized),
|
||||
nameof(TaskRowViewModel.CanOpenPlanningSession),
|
||||
nameof(TaskRowViewModel.CanResumeOrDiscardPlanning),
|
||||
nameof(TaskRowViewModel.CanQueuePlan),
|
||||
nameof(TaskRowViewModel.CanSendToQueue),
|
||||
nameof(TaskRowViewModel.CanFinalizePlanning),
|
||||
nameof(TaskRowViewModel.CanRefine),
|
||||
nameof(TaskRowViewModel.SendToQueueDisabledReason),
|
||||
nameof(TaskRowViewModel.RefineDisabledReason),
|
||||
nameof(TaskRowViewModel.PlanningDisabledReason),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsRefiningChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.IsRefining = true);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.IsRefining),
|
||||
nameof(TaskRowViewModel.CanRefine),
|
||||
nameof(TaskRowViewModel.RefineDisabledReason),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsManualChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.IsManual = true);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.IsManual),
|
||||
nameof(TaskRowViewModel.ManualBadge),
|
||||
nameof(TaskRowViewModel.CanRefine),
|
||||
nameof(TaskRowViewModel.CanSendToQueue),
|
||||
nameof(TaskRowViewModel.CanOpenPlanningSession),
|
||||
nameof(TaskRowViewModel.SendToQueueDisabledReason),
|
||||
nameof(TaskRowViewModel.RefineDisabledReason),
|
||||
nameof(TaskRowViewModel.PlanningDisabledReason),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasInteractiveSessionChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.HasInteractiveSession = true);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.HasInteractiveSession),
|
||||
nameof(TaskRowViewModel.IsParked),
|
||||
nameof(TaskRowViewModel.StatusChipClass),
|
||||
nameof(TaskRowViewModel.StatusLabel),
|
||||
nameof(TaskRowViewModel.ShowStatusChip),
|
||||
nameof(TaskRowViewModel.StatusChipTooltip),
|
||||
nameof(TaskRowViewModel.CanSendToQueue),
|
||||
nameof(TaskRowViewModel.SendToQueueDisabledReason),
|
||||
nameof(TaskRowViewModel.CanDeleteTask),
|
||||
nameof(TaskRowViewModel.DeleteDisabledReason),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FailureReasonChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.FailureReason = "error");
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.FailureReason),
|
||||
nameof(TaskRowViewModel.HasFailureReason),
|
||||
nameof(TaskRowViewModel.FailureReasonLabel),
|
||||
nameof(TaskRowViewModel.FailureReasonTooltip),
|
||||
nameof(TaskRowViewModel.StatusChipTooltip),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FailureTurnsUsedChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.FailureTurnsUsed = 5);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.FailureTurnsUsed),
|
||||
nameof(TaskRowViewModel.FailureReasonTooltip),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FailureMaxTurnsChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.FailureMaxTurns = 10);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.FailureMaxTurns),
|
||||
nameof(TaskRowViewModel.FailureReasonTooltip),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasQueuedSubtasksChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.HasQueuedSubtasks = true);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.HasQueuedSubtasks),
|
||||
nameof(TaskRowViewModel.CanRemoveFromQueue),
|
||||
nameof(TaskRowViewModel.CanSendToQueue),
|
||||
nameof(TaskRowViewModel.CanQueuePlan),
|
||||
nameof(TaskRowViewModel.SendToQueueDisabledReason),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsHoveredChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.IsHovered = true);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.IsHovered),
|
||||
nameof(TaskRowViewModel.ShowRowActions),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsSelectedChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.IsSelected = true);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.IsSelected),
|
||||
nameof(TaskRowViewModel.ShowRowActions),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BlockedByTaskIdChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.BlockedByTaskId = "blocker");
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.BlockedByTaskId),
|
||||
nameof(TaskRowViewModel.IsQueued),
|
||||
nameof(TaskRowViewModel.IsWaiting),
|
||||
nameof(TaskRowViewModel.StatusChipClass),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasPlanningChildrenChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.HasPlanningChildren = true);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.HasPlanningChildren),
|
||||
nameof(TaskRowViewModel.IsPlanningParent),
|
||||
nameof(TaskRowViewModel.CanQueuePlan),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NumberChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.Number = 5);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.Number),
|
||||
nameof(TaskRowViewModel.ShowNumberBadge),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BranchChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.Branch = "main");
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.Branch),
|
||||
nameof(TaskRowViewModel.HasBranch),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WorktreePathChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.WorktreePath = @"C:\repo\worktrees\t");
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.WorktreePath),
|
||||
nameof(TaskRowViewModel.CanOpenWorktree),
|
||||
nameof(TaskRowViewModel.OpenWorktreeDisabledReason),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WorktreeStateChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.WorktreeState = ClaudeDo.Data.Models.WorktreeState.Active);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.WorktreeState),
|
||||
nameof(TaskRowViewModel.IsParked),
|
||||
nameof(TaskRowViewModel.StatusLabel),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoneChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.Done = true);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.Done),
|
||||
nameof(TaskRowViewModel.IsOverdue),
|
||||
nameof(TaskRowViewModel.CanAddToMyDay),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsMyDayChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.IsMyDay = true);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.IsMyDay),
|
||||
nameof(TaskRowViewModel.CanAddToMyDay),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScheduledForChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.ScheduledFor = DateTime.UtcNow);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.ScheduledFor),
|
||||
nameof(TaskRowViewModel.IsOverdue),
|
||||
nameof(TaskRowViewModel.HasSchedule),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DiffAdditionsChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.DiffAdditions = 5);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.DiffAdditions),
|
||||
nameof(TaskRowViewModel.HasDiff),
|
||||
nameof(TaskRowViewModel.DiffAdditionsText),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DiffDeletionsChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.DiffDeletions = 5);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.DiffDeletions),
|
||||
nameof(TaskRowViewModel.HasDiff),
|
||||
nameof(TaskRowViewModel.DiffDeletionsText),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoadblockCountChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.RoadblockCount = 3);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.RoadblockCount),
|
||||
nameof(TaskRowViewModel.HasRoadblock),
|
||||
nameof(TaskRowViewModel.RoadblockTooltip),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropHintAboveChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.DropHintAbove = true);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.DropHintAbove),
|
||||
nameof(TaskRowViewModel.IsDropTarget),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropHintBelowChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.DropHintBelow = true);
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.DropHintBelow),
|
||||
nameof(TaskRowViewModel.IsDropTarget),
|
||||
}, raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreationPhaseChanged_Notifies_Dependents()
|
||||
{
|
||||
var vm = new TaskRowViewModel { Id = "t" };
|
||||
var raised = Raised(vm, () => vm.CreationPhase = "creating_worktree");
|
||||
Assert.Equal(new HashSet<string?>
|
||||
{
|
||||
nameof(TaskRowViewModel.CreationPhase),
|
||||
nameof(TaskRowViewModel.HasCreationPhase),
|
||||
nameof(TaskRowViewModel.CreationPhaseLabel),
|
||||
}, raised);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user