using AyCode.Blazor.Components.Components.Grids; using AyCode.Core.Tests.TestModels; using AyCode.Services.Server.Tests.SignalRs; using AyCode.Services.SignalRs; using Bunit; using DevExpress.Blazor; using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace AyCode.Blazor.Components.Tests.Grids; /// /// Tests for MgGridBase layout persistence functionality. /// Tests that column width changes are persisted and loaded correctly. /// [TestClass] public class MgGridBaseTests : BunitTestContext { private TestLogger _logger = null!; private TestableSignalRHub2 _hub = null!; private TestableSignalRClient2 _client = null!; private TestSignalRService2 _service = null!; private SignalRCrudTags _crudTags = null!; private const string StorageKey = "TestGrid_Master_AutoSave_0"; [TestInitialize] public void TestSetup() { Context = new BunitContext(); Context.Services.AddDevExpressBlazor(); _logger = new TestLogger(); _hub = new TestableSignalRHub2(); _service = new TestSignalRService2(); _client = new TestableSignalRClient2(_hub, _logger); _hub.RegisterService(_service, _client); _crudTags = new SignalRCrudTags( TestSignalRTags.DataSourceGetAll, TestSignalRTags.DataSourceGetItem, TestSignalRTags.DataSourceAdd, TestSignalRTags.DataSourceUpdate, TestSignalRTags.DataSourceRemove ); TestMgGridOrderItem.ClearLayoutStorage(); Context.JSInterop.Mode = JSRuntimeMode.Loose; } [TestCleanup] public void TestTeardown() => Context?.Dispose(); private IRenderedComponent RenderTestGrid( string autoSaveLayoutName = "TestGrid", System.Action? onDynamicColumnAttributeAdding = null) { var dataSource = new TestGridOrderItemDataSource(_client, _crudTags); return Context!.Render(parameters => { parameters .Add(p => p.DataSource, dataSource) .Add(p => p.Logger, _logger) .Add(p => p.SignalRClient, _client) .Add(p => p.AutoSaveLayoutName, autoSaveLayoutName) .Add(p => p.GetAllMessageTag, _crudTags.GetAllMessageTag) .Add(p => p.AddMessageTag, _crudTags.AddMessageTag) .Add(p => p.UpdateMessageTag, _crudTags.UpdateMessageTag) .Add(p => p.RemoveMessageTag, _crudTags.RemoveMessageTag); if (onDynamicColumnAttributeAdding != null) { parameters.Add(p => p.OnDynamicColumnAttributeAdding, onDynamicColumnAttributeAdding); } }); } #region Layout Persistence Tests [TestMethod] public async Task MgGridBase_ColumnWidth_ShouldBePersisted_WhenNewGridIsRendered() { // Arrange - Render first grid var cut1 = RenderTestGrid(); // Wait for data source to load await cut1.Instance.WaitForDataSourceLoadedAsync(); // Get columns from first grid var columns1 = cut1.Instance.GetDataColumns(); Assert.IsTrue(columns1.Count > 0, "Grid should have columns after data source loaded"); // Set column width on first grid (must use BeginUpdate/EndUpdate on the dispatcher) var firstColumn = columns1[0]; var expectedWidth = "150px"; await cut1.InvokeAsync(() => { cut1.Instance.BeginUpdate(); firstColumn.Width = expectedWidth; cut1.Instance.EndUpdate(); }); // Trigger layout save by disposing await cut1.Instance.DisposeAsync(); // Act - Render second grid with same TDataItem var cut2 = RenderTestGrid(); // Wait for data source to load await cut2.Instance.WaitForDataSourceLoadedAsync(); // Assert - Second grid should have the same column width var columns2 = cut2.Instance.GetDataColumns(); Assert.IsTrue(columns2.Count > 0, "Second grid should have columns"); var secondGridFirstColumn = columns2[0]; Assert.AreEqual(expectedWidth, secondGridFirstColumn.Width, $"Column width should be persisted. Expected: {expectedWidth}, Actual: {secondGridFirstColumn.Width}"); } [TestMethod] public async Task MgGridBase_LayoutStorage_ShouldContainLayout_AfterGridRenders() { // Arrange & Act var cut = RenderTestGrid(); await cut.Instance.WaitForDataSourceLoadedAsync(); // Assert Assert.IsTrue(TestMgGridOrderItem.LayoutStorage.ContainsKey(StorageKey), "Layout should be saved to storage after grid renders"); Assert.IsNotNull(TestMgGridOrderItem.LayoutStorage[StorageKey]); } [TestMethod] public async Task MgGridBase_DifferentGridNames_ShouldHaveDifferentLayouts() { // Arrange - Render first grid with name "Grid1" var cut1 = RenderTestGrid("Grid1"); await cut1.Instance.WaitForDataSourceLoadedAsync(); // Render second grid with name "Grid2" var cut2 = RenderTestGrid("Grid2"); await cut2.Instance.WaitForDataSourceLoadedAsync(); // Assert - Different storage keys should exist var storageKeys = TestMgGridOrderItem.LayoutStorage.Keys.ToList(); // There should be at least 2 different keys Assert.IsTrue(storageKeys.Count >= 2, $"Should have at least 2 storage keys, found: {string.Join(", ", storageKeys)}"); // Keys should contain Grid1 and Grid2 Assert.IsTrue(storageKeys.Any(k => k.Contains("Grid1")), $"Should have Grid1 key. Keys: {string.Join(", ", storageKeys)}"); Assert.IsTrue(storageKeys.Any(k => k.Contains("Grid2")), $"Should have Grid2 key. Keys: {string.Join(", ", storageKeys)}"); } [TestMethod] public async Task MgGridBase_IsMasterGrid_ShouldBeTrueWhenNoParentDataItem() { // Act var cut = RenderTestGrid(); await cut.Instance.WaitForDataSourceLoadedAsync(); // Assert Assert.IsTrue(cut.Instance.IsMasterGrid); } [TestMethod] public async Task MgGridBase_Columns_ShouldBeBuiltFromReflection() { // Arrange & Act var cut = RenderTestGrid(); await cut.Instance.WaitForDataSourceLoadedAsync(); // Assert - Should have columns for TestOrderItem properties var columns = cut.Instance.GetDataColumns(); Assert.IsTrue(columns.Count > 0, "Grid should have columns built from TDataItem"); // Verify some expected column names exist var columnNames = columns.Select(c => c.FieldName).ToList(); Assert.IsTrue(columnNames.Contains(nameof(TestOrderItem.Id)), "Should have Id column"); Assert.IsTrue(columnNames.Contains(nameof(TestOrderItem.ProductName)), "Should have ProductName column"); Assert.IsTrue(columnNames.Contains(nameof(TestOrderItem.Quantity)), "Should have Quantity column"); } #endregion #region MgGridDataColumn UrlLink Tests [TestMethod] public async Task MgGridDataColumn_UrlLink_ShouldRenderLinkWithReplacedValues() { // Arrange - Render grid with UrlLink on Id column var cut = RenderTestGrid(onDynamicColumnAttributeAdding: args => { if (args.FieldName == nameof(TestOrderItem.Id)) { args.AdditionalAttributes[nameof(MgGridDataColumn.UrlLink)] = "https://example.com/edit/{Id}"; } }); // Wait for data source to load await cut.Instance.WaitForDataSourceLoadedAsync(); // Get the first row's Id value from the grid var firstRowId = cut.Instance.GetRowValue(0, nameof(TestOrderItem.Id)); Assert.IsNotNull(firstRowId, "First row should have an Id value"); // Build the expected URL with the actual Id value var expectedUrl = $"https://example.com/edit/{firstRowId}"; // Find the anchor element with the exact expected href var anchor = cut.Find($"a[href=\"{expectedUrl}\"]"); // Assert - The anchor should exist and its text content should be the Id value Assert.IsNotNull(anchor, $"Should find anchor with href='{expectedUrl}'"); Assert.AreEqual(firstRowId.ToString(), anchor.TextContent, $"Anchor text should be the Id value '{firstRowId}', but was '{anchor.TextContent}'"); } #endregion }