refactor(configmanager): rename UI project and split test projects

Rename ConfigManager to ConfigManager.Ui to match the Core/CLI/UI project
structure, and split the monolithic test project into Core.Tests,
Cli.Tests, and Ui.Tests to align with the source project organization.
This commit is contained in:
Joseph Doherty
2026-01-28 10:24:36 -05:00
parent 7c4781dfe3
commit 1fc7792cd1
131 changed files with 267 additions and 212 deletions
@@ -0,0 +1,243 @@
using Avalonia.Controls;
using Avalonia.Headless.XUnit;
using Avalonia.VisualTree;
using JdeScoping.ConfigManager.Ui.Views.Dialogs;
namespace JdeScoping.ConfigManager.Ui.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.Ui.Views.Forms;
namespace JdeScoping.ConfigManager.Ui.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.Ui.Views;
namespace JdeScoping.ConfigManager.Ui.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();
}
}