feat(configmanager): add TreeNodeViewModel
This commit is contained in:
@@ -2,17 +2,35 @@ using System.Collections.ObjectModel;
|
|||||||
|
|
||||||
namespace JdeScoping.ConfigManager.ViewModels;
|
namespace JdeScoping.ConfigManager.ViewModels;
|
||||||
|
|
||||||
|
public enum TreeNodeType
|
||||||
|
{
|
||||||
|
Folder,
|
||||||
|
SettingsSection,
|
||||||
|
Pipeline
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ValidationState
|
||||||
|
{
|
||||||
|
Valid,
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
Unknown
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ViewModel for a tree node in the configuration tree.
|
/// ViewModel for a tree node in the configuration tree.
|
||||||
/// This is a stub implementation for Task 11 - full implementation in Task 12.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TreeNodeViewModel : ViewModelBase
|
public class TreeNodeViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private bool _isModified;
|
private bool _isModified;
|
||||||
|
private bool _isExpanded;
|
||||||
|
private bool _isSelected;
|
||||||
|
private ValidationState _validationState = ValidationState.Unknown;
|
||||||
|
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; }
|
||||||
public string Icon { get; set; } = string.Empty;
|
public string Icon { get; }
|
||||||
public string StatusIcon { get; set; } = string.Empty;
|
public TreeNodeType NodeType { get; }
|
||||||
|
public string? SectionKey { get; init; }
|
||||||
public ObservableCollection<TreeNodeViewModel> Children { get; } = [];
|
public ObservableCollection<TreeNodeViewModel> Children { get; } = [];
|
||||||
|
|
||||||
public bool IsModified
|
public bool IsModified
|
||||||
@@ -20,4 +38,41 @@ public class TreeNodeViewModel : ViewModelBase
|
|||||||
get => _isModified;
|
get => _isModified;
|
||||||
set => SetProperty(ref _isModified, value);
|
set => SetProperty(ref _isModified, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsExpanded
|
||||||
|
{
|
||||||
|
get => _isExpanded;
|
||||||
|
set => SetProperty(ref _isExpanded, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get => _isSelected;
|
||||||
|
set => SetProperty(ref _isSelected, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationState ValidationState
|
||||||
|
{
|
||||||
|
get => _validationState;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _validationState, value))
|
||||||
|
OnPropertyChanged(nameof(StatusIcon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusIcon => ValidationState switch
|
||||||
|
{
|
||||||
|
ValidationState.Valid => "✓",
|
||||||
|
ValidationState.Warning => "⚠",
|
||||||
|
ValidationState.Error => "✗",
|
||||||
|
_ => ""
|
||||||
|
};
|
||||||
|
|
||||||
|
public TreeNodeViewModel(string name, string icon, TreeNodeType nodeType)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Icon = icon;
|
||||||
|
NodeType = nodeType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,221 @@
|
|||||||
|
using JdeScoping.ConfigManager.ViewModels;
|
||||||
|
|
||||||
|
namespace JdeScoping.ConfigManager.Tests.ViewModels;
|
||||||
|
|
||||||
|
public class TreeNodeViewModelTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_SetsProperties()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var node = new TreeNodeViewModel("DataSync", "⟳", TreeNodeType.SettingsSection);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
node.Name.ShouldBe("DataSync");
|
||||||
|
node.Icon.ShouldBe("⟳");
|
||||||
|
node.NodeType.ShouldBe(TreeNodeType.SettingsSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsModified_WhenSet_RaisesPropertyChanged()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
||||||
|
var propertyChangedRaised = false;
|
||||||
|
node.PropertyChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(TreeNodeViewModel.IsModified))
|
||||||
|
propertyChangedRaised = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
node.IsModified = true;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
propertyChangedRaised.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsExpanded_WhenSet_RaisesPropertyChanged()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
||||||
|
var propertyChangedRaised = false;
|
||||||
|
node.PropertyChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(TreeNodeViewModel.IsExpanded))
|
||||||
|
propertyChangedRaised = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
node.IsExpanded = true;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
propertyChangedRaised.ShouldBeTrue();
|
||||||
|
node.IsExpanded.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsSelected_WhenSet_RaisesPropertyChanged()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
||||||
|
var propertyChangedRaised = false;
|
||||||
|
node.PropertyChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(TreeNodeViewModel.IsSelected))
|
||||||
|
propertyChangedRaised = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
node.IsSelected = true;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
propertyChangedRaised.ShouldBeTrue();
|
||||||
|
node.IsSelected.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ValidationState_WhenSetToValid_StatusIconIsCheckmark()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
node.ValidationState = ValidationState.Valid;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
node.StatusIcon.ShouldBe("✓");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ValidationState_WhenSetToWarning_StatusIconIsWarning()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
node.ValidationState = ValidationState.Warning;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
node.StatusIcon.ShouldBe("⚠");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ValidationState_WhenSetToError_StatusIconIsX()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
node.ValidationState = ValidationState.Error;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
node.StatusIcon.ShouldBe("✗");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ValidationState_WhenSetToUnknown_StatusIconIsEmpty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
node.ValidationState = ValidationState.Unknown;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
node.StatusIcon.ShouldBe("");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ValidationState_WhenChanged_RaisesPropertyChangedForStatusIcon()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
||||||
|
var statusIconPropertyChanged = false;
|
||||||
|
node.PropertyChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(TreeNodeViewModel.StatusIcon))
|
||||||
|
statusIconPropertyChanged = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
node.ValidationState = ValidationState.Valid;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
statusIconPropertyChanged.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SectionKey_CanBeSetViaInitializer()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var node = new TreeNodeViewModel("DataSync", "⟳", TreeNodeType.SettingsSection)
|
||||||
|
{
|
||||||
|
SectionKey = "DataSync"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
node.SectionKey.ShouldBe("DataSync");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Children_IsInitialized()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
node.Children.ShouldNotBeNull();
|
||||||
|
node.Children.ShouldBeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Children_CanAddChildNodes()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var parent = new TreeNodeViewModel("Settings", "📁", TreeNodeType.Folder);
|
||||||
|
var child = new TreeNodeViewModel("DataSync", "⟳", TreeNodeType.SettingsSection);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
parent.Children.Add(child);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
parent.Children.Count.ShouldBe(1);
|
||||||
|
parent.Children[0].ShouldBe(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(TreeNodeType.Folder)]
|
||||||
|
[InlineData(TreeNodeType.SettingsSection)]
|
||||||
|
[InlineData(TreeNodeType.Pipeline)]
|
||||||
|
public void NodeType_CanBeSetForAllTypes(TreeNodeType nodeType)
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var node = new TreeNodeViewModel("Test", "📁", nodeType);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
node.NodeType.ShouldBe(nodeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsModified_WhenSetToSameValue_DoesNotRaisePropertyChanged()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var node = new TreeNodeViewModel("Test", "📁", TreeNodeType.Folder);
|
||||||
|
node.IsModified = true;
|
||||||
|
|
||||||
|
var propertyChangedRaised = false;
|
||||||
|
node.PropertyChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(TreeNodeViewModel.IsModified))
|
||||||
|
propertyChangedRaised = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
node.IsModified = true; // Same value
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
propertyChangedRaised.ShouldBeFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user