test(configmanager): expand unit test coverage to 451 tests

Add comprehensive tests for services (ConnectionTestService, RuntimeConfigValidation),
ViewModels (PipelineEditor, dialogs, transformers), and Avalonia headless UI tests
for views and forms.
This commit is contained in:
Joseph Doherty
2026-01-27 07:24:55 -05:00
parent 227a749cdf
commit 937eb66ac8
14 changed files with 4053 additions and 62 deletions
@@ -0,0 +1,243 @@
using Avalonia.Controls;
using Avalonia.Headless.XUnit;
using Avalonia.VisualTree;
using JdeScoping.ConfigManager.Views.Dialogs;
namespace JdeScoping.ConfigManager.Tests.Views.Dialogs;
/// <summary>
/// UI tests for dialog views.
/// </summary>
public class DialogViewTests
{
/// <summary>
/// Verifies that NewStoreDialog renders with the expected input fields.
/// </summary>
[AvaloniaFact]
public void NewStoreDialogView_RendersInputFields()
{
// Arrange & Act
var dialog = new NewStoreDialog();
dialog.Show();
// Assert - NewStoreDialog contains TextBoxes for store path and key file path
var textBoxes = dialog.GetVisualDescendants().OfType<TextBox>().ToList();
textBoxes.Count.ShouldBeGreaterThanOrEqualTo(2); // Store path and Key file path
// Verify section headers
var textBlocks = dialog.GetVisualDescendants().OfType<TextBlock>().ToList();
textBlocks.Any(tb => tb.Text == "Store Location").ShouldBeTrue();
textBlocks.Any(tb => tb.Text == "Key File").ShouldBeTrue();
}
/// <summary>
/// Verifies that NewStoreDialog has Browse and Generate buttons for key file.
/// </summary>
[AvaloniaFact]
public void NewStoreDialogView_HasBrowseAndGenerateButtons()
{
// Arrange & Act
var dialog = new NewStoreDialog();
dialog.Show();
// Assert
var buttons = dialog.GetVisualDescendants().OfType<Button>().ToList();
var buttonContents = buttons.Select(b => b.Content?.ToString()).ToList();
buttonContents.ShouldContain("Browse...");
buttonContents.ShouldContain("Generate");
}
/// <summary>
/// Verifies that NewStoreDialog has Create and Cancel buttons.
/// </summary>
[AvaloniaFact]
public void NewStoreDialogView_HasCreateAndCancelButtons()
{
// Arrange & Act
var dialog = new NewStoreDialog();
dialog.Show();
// Assert
var buttons = dialog.GetVisualDescendants().OfType<Button>().ToList();
var buttonContents = buttons.Select(b => b.Content?.ToString()).ToList();
buttonContents.ShouldContain("Create");
buttonContents.ShouldContain("Cancel");
}
/// <summary>
/// Verifies that NewStoreDialog has the correct title.
/// </summary>
[AvaloniaFact]
public void NewStoreDialogView_HasCorrectTitle()
{
// Arrange & Act
var dialog = new NewStoreDialog();
dialog.Show();
// Assert
dialog.Title.ShouldBe("Create New Secure Store");
}
/// <summary>
/// Verifies that NewStoreDialog has the expected size constraints.
/// </summary>
[AvaloniaFact]
public void NewStoreDialogView_HasExpectedSize()
{
// Arrange & Act
var dialog = new NewStoreDialog();
dialog.Show();
// Assert - Width=550, Height=350, MinWidth=450, MinHeight=300
dialog.Width.ShouldBe(550);
dialog.Height.ShouldBe(350);
dialog.MinWidth.ShouldBe(450);
dialog.MinHeight.ShouldBe(300);
}
/// <summary>
/// Verifies that UnlockStoreDialog renders with key file input field.
/// </summary>
[AvaloniaFact]
public void UnlockStoreDialogView_RendersKeyFileField()
{
// Arrange & Act
var dialog = new UnlockStoreDialog();
dialog.Show();
// Assert - UnlockStoreDialog contains TextBoxes for store path (read-only) and key file path
var textBoxes = dialog.GetVisualDescendants().OfType<TextBox>().ToList();
textBoxes.Count.ShouldBeGreaterThanOrEqualTo(2); // Store path and Key file path
// Verify section headers
var textBlocks = dialog.GetVisualDescendants().OfType<TextBlock>().ToList();
textBlocks.Any(tb => tb.Text == "Store File").ShouldBeTrue();
textBlocks.Any(tb => tb.Text == "Key File").ShouldBeTrue();
}
/// <summary>
/// Verifies that UnlockStoreDialog has a Browse button for key file.
/// </summary>
[AvaloniaFact]
public void UnlockStoreDialogView_HasBrowseButton()
{
// Arrange & Act
var dialog = new UnlockStoreDialog();
dialog.Show();
// Assert
var buttons = dialog.GetVisualDescendants().OfType<Button>().ToList();
var buttonContents = buttons.Select(b => b.Content?.ToString()).ToList();
buttonContents.ShouldContain("Browse...");
}
/// <summary>
/// Verifies that UnlockStoreDialog has Unlock and Cancel buttons.
/// </summary>
[AvaloniaFact]
public void UnlockStoreDialogView_HasUnlockAndCancelButtons()
{
// Arrange & Act
var dialog = new UnlockStoreDialog();
dialog.Show();
// Assert
var buttons = dialog.GetVisualDescendants().OfType<Button>().ToList();
var buttonContents = buttons.Select(b => b.Content?.ToString()).ToList();
buttonContents.ShouldContain("Unlock");
buttonContents.ShouldContain("Cancel");
}
/// <summary>
/// Verifies that UnlockStoreDialog has the correct title.
/// </summary>
[AvaloniaFact]
public void UnlockStoreDialogView_HasCorrectTitle()
{
// Arrange & Act
var dialog = new UnlockStoreDialog();
dialog.Show();
// Assert
dialog.Title.ShouldBe("Unlock Secure Store");
}
/// <summary>
/// Verifies that UnlockStoreDialog has the expected size constraints.
/// </summary>
[AvaloniaFact]
public void UnlockStoreDialogView_HasExpectedSize()
{
// Arrange & Act
var dialog = new UnlockStoreDialog();
dialog.Show();
// Assert - Width=500, Height=320, MinWidth=400, MinHeight=280
dialog.Width.ShouldBe(500);
dialog.Height.ShouldBe(320);
dialog.MinWidth.ShouldBe(400);
dialog.MinHeight.ShouldBe(280);
}
/// <summary>
/// Verifies that NewStoreDialog is not resizable.
/// </summary>
[AvaloniaFact]
public void NewStoreDialogView_IsNotResizable()
{
// Arrange & Act
var dialog = new NewStoreDialog();
dialog.Show();
// Assert
dialog.CanResize.ShouldBeFalse();
}
/// <summary>
/// Verifies that UnlockStoreDialog is not resizable.
/// </summary>
[AvaloniaFact]
public void UnlockStoreDialogView_IsNotResizable()
{
// Arrange & Act
var dialog = new UnlockStoreDialog();
dialog.Show();
// Assert
dialog.CanResize.ShouldBeFalse();
}
/// <summary>
/// Verifies that NewStoreDialog contains a ScrollViewer.
/// </summary>
[AvaloniaFact]
public void NewStoreDialogView_ContainsScrollViewer()
{
// Arrange & Act
var dialog = new NewStoreDialog();
dialog.Show();
// Assert
var scrollViewer = dialog.FindDescendantOfType<ScrollViewer>();
scrollViewer.ShouldNotBeNull();
}
/// <summary>
/// Verifies that UnlockStoreDialog contains a ScrollViewer.
/// </summary>
[AvaloniaFact]
public void UnlockStoreDialogView_ContainsScrollViewer()
{
// Arrange & Act
var dialog = new UnlockStoreDialog();
dialog.Show();
// Assert
var scrollViewer = dialog.FindDescendantOfType<ScrollViewer>();
scrollViewer.ShouldNotBeNull();
}
}
@@ -0,0 +1,233 @@
using Avalonia.Controls;
using Avalonia.Headless.XUnit;
using Avalonia.VisualTree;
using JdeScoping.ConfigManager.Views.Forms;
namespace JdeScoping.ConfigManager.Tests.Views.Forms;
/// <summary>
/// UI tests for form views.
/// </summary>
public class FormViewTests
{
/// <summary>
/// Creates a test window to host a UserControl and shows it.
/// UserControls must be attached to a Window to have their visual tree built.
/// </summary>
private static Window ShowInTestWindow(Control content)
{
var window = new Window { Content = content };
window.Show();
return window;
}
/// <summary>
/// Verifies that AuthFormView renders with the expected controls.
/// </summary>
[AvaloniaFact]
public void AuthFormView_RendersWithCorrectControls()
{
// Arrange
var view = new AuthFormView();
ShowInTestWindow(view);
// Assert - AuthFormView contains TextBox for CookieName and NumericUpDown for expiration
var textBoxes = view.GetVisualDescendants().OfType<TextBox>().ToList();
textBoxes.Count.ShouldBeGreaterThan(0);
var numericUpDowns = view.GetVisualDescendants().OfType<NumericUpDown>().ToList();
numericUpDowns.Count.ShouldBeGreaterThan(0);
// Verify header is present
var textBlocks = view.GetVisualDescendants().OfType<TextBlock>().ToList();
textBlocks.Any(tb => tb.Text == "Authentication Settings").ShouldBeTrue();
textBlocks.Any(tb => tb.Text == "Cookie Settings").ShouldBeTrue();
}
/// <summary>
/// Verifies that DataSyncFormView renders with the expected controls.
/// </summary>
[AvaloniaFact]
public void DataSyncFormView_RendersWithCorrectControls()
{
// Arrange
var view = new DataSyncFormView();
ShowInTestWindow(view);
// Assert - DataSyncFormView contains CheckBox for Enabled and multiple NumericUpDowns
var checkBoxes = view.GetVisualDescendants().OfType<CheckBox>().ToList();
checkBoxes.Count.ShouldBeGreaterThan(0);
var numericUpDowns = view.GetVisualDescendants().OfType<NumericUpDown>().ToList();
numericUpDowns.Count.ShouldBeGreaterThanOrEqualTo(6); // Check Interval, Timeout, Max Parallelism, Batch Size, Bulk Copy Batch Size, Lookback Multiplier, Purge Retention
// Verify section headers
var textBlocks = view.GetVisualDescendants().OfType<TextBlock>().ToList();
textBlocks.Any(tb => tb.Text == "Data Sync Settings").ShouldBeTrue();
textBlocks.Any(tb => tb.Text == "Sync Intervals").ShouldBeTrue();
textBlocks.Any(tb => tb.Text == "Performance").ShouldBeTrue();
textBlocks.Any(tb => tb.Text == "Data Retention").ShouldBeTrue();
}
/// <summary>
/// Verifies that ConnectionStringsFormView renders with a DataGrid.
/// </summary>
[AvaloniaFact]
public void ConnectionStringsFormView_RendersDataGrid()
{
// Arrange
var view = new ConnectionStringsFormView();
ShowInTestWindow(view);
// Assert - ConnectionStringsFormView contains a DataGrid for connections
var dataGrid = view.FindDescendantOfType<DataGrid>();
dataGrid.ShouldNotBeNull();
// Verify header is present
var textBlocks = view.GetVisualDescendants().OfType<TextBlock>().ToList();
textBlocks.Any(tb => tb.Text == "Connection Strings").ShouldBeTrue();
textBlocks.Any(tb => tb.Text == "Connections").ShouldBeTrue();
}
/// <summary>
/// Verifies that ConnectionStringsFormView has Add and Delete buttons.
/// </summary>
[AvaloniaFact]
public void ConnectionStringsFormView_HasAddAndDeleteButtons()
{
// Arrange
var view = new ConnectionStringsFormView();
ShowInTestWindow(view);
// Assert
var buttons = view.GetVisualDescendants().OfType<Button>().ToList();
// Find buttons containing TextBlock with Add/Delete text
var buttonTexts = buttons
.SelectMany(b => b.GetVisualDescendants().OfType<TextBlock>())
.Select(tb => tb.Text)
.ToList();
buttonTexts.ShouldContain("Add");
buttonTexts.ShouldContain("Delete");
}
/// <summary>
/// Verifies that LdapFormView renders with server URL list input.
/// </summary>
[AvaloniaFact]
public void LdapFormView_RendersServerList()
{
// Arrange
var view = new LdapFormView();
ShowInTestWindow(view);
// Assert - LdapFormView contains TextBoxes for server URLs, Group DN, Search Base
var textBoxes = view.GetVisualDescendants().OfType<TextBox>().ToList();
textBoxes.Count.ShouldBeGreaterThanOrEqualTo(3); // Server URLs, Group DN, Search Base, Admin Bypass Users
// Verify section headers
var textBlocks = view.GetVisualDescendants().OfType<TextBlock>().ToList();
textBlocks.Any(tb => tb.Text == "LDAP Settings").ShouldBeTrue();
textBlocks.Any(tb => tb.Text == "Server Configuration").ShouldBeTrue();
textBlocks.Any(tb => tb.Text == "Directory Structure").ShouldBeTrue();
}
/// <summary>
/// Verifies that LdapFormView contains a CheckBox for fake authentication.
/// </summary>
[AvaloniaFact]
public void LdapFormView_HasFakeAuthCheckBox()
{
// Arrange
var view = new LdapFormView();
ShowInTestWindow(view);
// Assert
var checkBoxes = view.GetVisualDescendants().OfType<CheckBox>().ToList();
checkBoxes.Count.ShouldBeGreaterThan(0);
// Verify development options section
var textBlocks = view.GetVisualDescendants().OfType<TextBlock>().ToList();
textBlocks.Any(tb => tb.Text == "Development Options").ShouldBeTrue();
}
/// <summary>
/// Verifies that LdapFormView contains a NumericUpDown for connection timeout.
/// </summary>
[AvaloniaFact]
public void LdapFormView_HasConnectionTimeoutNumericUpDown()
{
// Arrange
var view = new LdapFormView();
ShowInTestWindow(view);
// Assert
var numericUpDowns = view.GetVisualDescendants().OfType<NumericUpDown>().ToList();
numericUpDowns.Count.ShouldBeGreaterThan(0);
// Verify timeout label is present
var textBlocks = view.GetVisualDescendants().OfType<TextBlock>().ToList();
textBlocks.Any(tb => tb.Text == "Connection Timeout (seconds)").ShouldBeTrue();
}
/// <summary>
/// Verifies that AuthFormView contains a ScrollViewer.
/// </summary>
[AvaloniaFact]
public void AuthFormView_ContainsScrollViewer()
{
// Arrange
var view = new AuthFormView();
ShowInTestWindow(view);
// Assert
var scrollViewer = view.FindDescendantOfType<ScrollViewer>();
scrollViewer.ShouldNotBeNull();
}
/// <summary>
/// Verifies that DataSyncFormView contains a ScrollViewer.
/// </summary>
[AvaloniaFact]
public void DataSyncFormView_ContainsScrollViewer()
{
// Arrange
var view = new DataSyncFormView();
ShowInTestWindow(view);
// Assert
var scrollViewer = view.FindDescendantOfType<ScrollViewer>();
scrollViewer.ShouldNotBeNull();
}
/// <summary>
/// Verifies that ConnectionStringsFormView contains a ScrollViewer.
/// </summary>
[AvaloniaFact]
public void ConnectionStringsFormView_ContainsScrollViewer()
{
// Arrange
var view = new ConnectionStringsFormView();
ShowInTestWindow(view);
// Assert
var scrollViewer = view.FindDescendantOfType<ScrollViewer>();
scrollViewer.ShouldNotBeNull();
}
/// <summary>
/// Verifies that LdapFormView contains a ScrollViewer.
/// </summary>
[AvaloniaFact]
public void LdapFormView_ContainsScrollViewer()
{
// Arrange
var view = new LdapFormView();
ShowInTestWindow(view);
// Assert
var scrollViewer = view.FindDescendantOfType<ScrollViewer>();
scrollViewer.ShouldNotBeNull();
}
}
@@ -0,0 +1,161 @@
using Avalonia.Controls;
using Avalonia.Headless.XUnit;
using Avalonia.VisualTree;
using JdeScoping.ConfigManager.Views;
namespace JdeScoping.ConfigManager.Tests.Views;
/// <summary>
/// UI tests for <see cref="MainWindow"/>.
/// </summary>
public class MainWindowTests
{
/// <summary>
/// Verifies that the main window displays with the correct title.
/// </summary>
[AvaloniaFact]
public void MainWindow_ShowsWithCorrectTitle()
{
// Arrange & Act
var window = new MainWindow();
window.Show();
// Assert
window.Title.ShouldBe("JdeScoping ConfigManager");
}
/// <summary>
/// Verifies that the main window contains a TreeView for navigation.
/// </summary>
[AvaloniaFact]
public void MainWindow_ContainsTreeView()
{
// Arrange & Act
var window = new MainWindow();
window.Show();
// Assert
var treeView = window.FindDescendantOfType<TreeView>();
treeView.ShouldNotBeNull();
}
/// <summary>
/// Verifies that the main window contains a menu bar.
/// </summary>
[AvaloniaFact]
public void MainWindow_ContainsMenuBar()
{
// Arrange & Act
var window = new MainWindow();
window.Show();
// Assert
var menu = window.FindDescendantOfType<Menu>();
menu.ShouldNotBeNull();
}
/// <summary>
/// Verifies that the main window has the expected minimum size constraints.
/// </summary>
[AvaloniaFact]
public void MainWindow_HasExpectedMinimumSize()
{
// Arrange & Act
var window = new MainWindow();
window.Show();
// Assert - MinWidth=900, MinHeight=600 as defined in AXAML
window.MinWidth.ShouldBe(900);
window.MinHeight.ShouldBe(600);
}
/// <summary>
/// Verifies that the main window has the expected default size.
/// </summary>
[AvaloniaFact]
public void MainWindow_HasExpectedDefaultSize()
{
// Arrange & Act
var window = new MainWindow();
window.Show();
// Assert - Width=1200, Height=800 as defined in AXAML
window.Width.ShouldBe(1200);
window.Height.ShouldBe(800);
}
/// <summary>
/// Verifies that the main window contains toolbar buttons.
/// </summary>
[AvaloniaFact]
public void MainWindow_ContainsToolbarButtons()
{
// Arrange & Act
var window = new MainWindow();
window.Show();
// Assert
var buttons = window.GetVisualDescendants().OfType<Button>().ToList();
buttons.Count.ShouldBeGreaterThan(0);
// Verify toolbar buttons exist with expected content
buttons.Any(b => b.Content?.ToString() == "Open").ShouldBeTrue();
buttons.Any(b => b.Content?.ToString() == "Save").ShouldBeTrue();
buttons.Any(b => b.Content?.ToString() == "Undo").ShouldBeTrue();
buttons.Any(b => b.Content?.ToString() == "Redo").ShouldBeTrue();
buttons.Any(b => b.Content?.ToString() == "Validate").ShouldBeTrue();
}
/// <summary>
/// Verifies that the main window contains a ContentControl for form display.
/// </summary>
[AvaloniaFact]
public void MainWindow_ContainsFormContentControl()
{
// Arrange & Act
var window = new MainWindow();
window.Show();
// Assert
var contentControl = window.FindDescendantOfType<ContentControl>();
contentControl.ShouldNotBeNull();
}
/// <summary>
/// Verifies that the main window contains a GridSplitter for resizing panels.
/// </summary>
[AvaloniaFact]
public void MainWindow_ContainsGridSplitter()
{
// Arrange & Act
var window = new MainWindow();
window.Show();
// Assert
var splitter = window.FindDescendantOfType<GridSplitter>();
splitter.ShouldNotBeNull();
}
/// <summary>
/// Verifies that the menu contains expected menu items.
/// </summary>
[AvaloniaFact]
public void MainWindow_ContainsExpectedMenuItems()
{
// Arrange & Act
var window = new MainWindow();
window.Show();
// Assert
var menuItems = window.GetVisualDescendants().OfType<MenuItem>().ToList();
menuItems.Count.ShouldBeGreaterThan(0);
// Verify top-level menu items exist
menuItems.Any(m => m.Header?.ToString() == "_File").ShouldBeTrue();
menuItems.Any(m => m.Header?.ToString() == "_Edit").ShouldBeTrue();
menuItems.Any(m => m.Header?.ToString() == "_Tools").ShouldBeTrue();
menuItems.Any(m => m.Header?.ToString() == "_Pipelines").ShouldBeTrue();
menuItems.Any(m => m.Header?.ToString() == "_Secure Stores").ShouldBeTrue();
menuItems.Any(m => m.Header?.ToString() == "_Help").ShouldBeTrue();
}
}