From 3f62b43a2e727cc54e0f2bb12de5a967a5730726 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:28:43 +0000 Subject: [PATCH 1/5] Initial plan From 6897d3e445d1ab41da70968544068253bc46f56b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:32:00 +0000 Subject: [PATCH 2/5] Add comprehensive tests for Contracts View Page following Estate Index patterns Co-authored-by: StuartFerguson <16325469+StuartFerguson@users.noreply.github.com> --- .../Pages/Contracts/ContractsViewPageTests.cs | 239 ++++++++++++++++++ 1 file changed, 239 insertions(+) diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs index 8dbd80ce..e355e0f0 100644 --- a/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs @@ -1,6 +1,7 @@ using Bunit; using EstateManagementUI.BlazorServer.Components.Pages.Contracts; using EstateManagementUI.BlazorServer.Components.Permissions; +using EstateManagementUI.BlazorServer.Models; using EstateManagementUI.BlazorServer.Permissions; using EstateManagementUI.BlazorServer.Tests.Pages.FileProcessing; using EstateManagementUI.BusinessLogic.Models; @@ -79,4 +80,242 @@ public void ContractsView_HasCorrectPageTitle() var pageTitle = cut.FindComponent(); pageTitle.Instance.ChildContent.ShouldNotBeNull(); } + + [Fact] + public void ContractsView_HasBackButton() + { + // Arrange + var contractId = Guid.NewGuid(); + var contract = new ContractModel + { + ContractId = contractId, + Description = "Test Contract", + OperatorName = "Test Operator" + }; + + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(contract)); + + // Act + var cut = RenderComponent(parameters => parameters + .Add(p => p.ContractId, contractId)); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + // Assert + cut.Markup.ShouldContain("Back to List"); + } + + [Fact] + public void ContractsView_BackButton_NavigatesToContractsList() + { + // Arrange + var contractId = Guid.NewGuid(); + var contract = new ContractModel + { + ContractId = contractId, + Description = "Test Contract", + OperatorName = "Test Operator" + }; + + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(contract)); + + // Act + var cut = RenderComponent(parameters => parameters + .Add(p => p.ContractId, contractId)); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + // Find and click the Back to List button + IRefreshableElementCollection buttons = cut.FindAll("button"); + IElement? backButton = buttons.FirstOrDefault(b => b.TextContent.Contains("Back to List")); + backButton?.Click(); + + // Assert + _fakeNavigationManager.Uri.ShouldContain("/contracts"); + } + + [Fact] + public void ContractsView_DisplaysProducts_WhenPresent() + { + // Arrange + var contractId = Guid.NewGuid(); + var contract = new ContractModel + { + ContractId = contractId, + Description = "Test Contract", + OperatorName = "Test Operator", + Products = new List + { + new ContractProductModel + { + ContractProductId = Guid.NewGuid(), + ProductName = "Test Product", + DisplayText = "Test Display", + ProductType = ProductType.MobileTopup, + Value = "100", + NumberOfFees = 2 + } + } + }; + + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(contract)); + + // Act + var cut = RenderComponent(parameters => parameters + .Add(p => p.ContractId, contractId)); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + // Assert + cut.Markup.ShouldContain("Test Product"); + cut.Markup.ShouldContain("Test Display"); + cut.Markup.ShouldContain("100"); + } + + [Fact] + public void ContractsView_DisplaysNoProducts_WhenEmpty() + { + // Arrange + var contractId = Guid.NewGuid(); + var contract = new ContractModel + { + ContractId = contractId, + Description = "Test Contract", + OperatorName = "Test Operator", + Products = new List() + }; + + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(contract)); + + // Act + var cut = RenderComponent(parameters => parameters + .Add(p => p.ContractId, contractId)); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + // Assert + cut.Markup.ShouldContain("No products added to this contract yet"); + } + + [Fact] + public void ContractsView_DisplaysTransactionFees_WhenPresent() + { + // Arrange + var contractId = Guid.NewGuid(); + var contract = new ContractModel + { + ContractId = contractId, + Description = "Test Contract", + OperatorName = "Test Operator", + Products = new List + { + new ContractProductModel + { + ContractProductId = Guid.NewGuid(), + ProductName = "Test Product", + DisplayText = "Test Display", + ProductType = ProductType.MobileTopup, + Value = "100", + NumberOfFees = 1, + TransactionFees = new List + { + new ContractProductTransactionFeeModel + { + TransactionFeeId = Guid.NewGuid(), + Description = "Service Fee", + CalculationType = CalculationType.Fixed, + FeeType = FeeType.ServiceProvider, + Value = 10.50m + } + } + } + } + }; + + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(contract)); + + // Act + var cut = RenderComponent(parameters => parameters + .Add(p => p.ContractId, contractId)); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + // Assert + cut.Markup.ShouldContain("Transaction Fees"); + cut.Markup.ShouldContain("Service Fee"); + cut.Markup.ShouldContain("10.50"); + } + + [Fact] + public void ContractsView_LoadContract_QueryFails_NavigatesToError() + { + // Arrange + var contractId = Guid.NewGuid(); + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Failure("Failed to load contract")); + + // Act + var cut = RenderComponent(parameters => parameters + .Add(p => p.ContractId, contractId)); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + // Assert + cut.Markup.ShouldContain("Contract not found"); + } + + [Fact] + public void ContractsView_ContractNotFound_ShowsMessage() + { + // Arrange + var contractId = Guid.NewGuid(); + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(null!)); + + // Act + var cut = RenderComponent(parameters => parameters + .Add(p => p.ContractId, contractId)); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + // Assert + cut.Markup.ShouldContain("Contract not found"); + } + + [Fact] + public void ContractsView_ContractNotFound_HasBackButton() + { + // Arrange + var contractId = Guid.NewGuid(); + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(null!)); + + // Act + var cut = RenderComponent(parameters => parameters + .Add(p => p.ContractId, contractId)); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + // Assert + cut.Markup.ShouldContain("Back to List"); + } + + [Fact] + public void ContractsView_ContractNotFound_BackButton_NavigatesToContractsList() + { + // Arrange + var contractId = Guid.NewGuid(); + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(null!)); + + // Act + var cut = RenderComponent(parameters => parameters + .Add(p => p.ContractId, contractId)); + cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); + + // Find and click the Back to List button + IRefreshableElementCollection buttons = cut.FindAll("button"); + IElement? backButton = buttons.FirstOrDefault(b => b.TextContent.Contains("Back to List")); + backButton?.Click(); + + // Assert + _fakeNavigationManager.Uri.ShouldContain("/contracts"); + } } From a7bbbedd1fb45515ccd3a31775e4b1a9a614b268 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:33:07 +0000 Subject: [PATCH 3/5] Address code review feedback - rename tests for clarity Co-authored-by: StuartFerguson <16325469+StuartFerguson@users.noreply.github.com> --- .../Pages/Contracts/ContractsViewPageTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs index e355e0f0..6d5cab75 100644 --- a/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs @@ -247,7 +247,7 @@ public void ContractsView_DisplaysTransactionFees_WhenPresent() } [Fact] - public void ContractsView_LoadContract_QueryFails_NavigatesToError() + public void ContractsView_LoadContract_QueryFails_ShowsNotFoundMessage() { // Arrange var contractId = Guid.NewGuid(); @@ -264,7 +264,7 @@ public void ContractsView_LoadContract_QueryFails_NavigatesToError() } [Fact] - public void ContractsView_ContractNotFound_ShowsMessage() + public void ContractsView_LoadContract_ReturnsNull_ShowsNotFoundMessage() { // Arrange var contractId = Guid.NewGuid(); From 4333e899cebb3d574a35841bd8c70ffe1355d4ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:34:00 +0000 Subject: [PATCH 4/5] Add null checks for buttons before clicking to ensure they exist Co-authored-by: StuartFerguson <16325469+StuartFerguson@users.noreply.github.com> --- .../Pages/Contracts/ContractsViewPageTests.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs index 6d5cab75..d4228f01 100644 --- a/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs @@ -128,7 +128,8 @@ public void ContractsView_BackButton_NavigatesToContractsList() // Find and click the Back to List button IRefreshableElementCollection buttons = cut.FindAll("button"); IElement? backButton = buttons.FirstOrDefault(b => b.TextContent.Contains("Back to List")); - backButton?.Click(); + backButton.ShouldNotBeNull(); + backButton.Click(); // Assert _fakeNavigationManager.Uri.ShouldContain("/contracts"); @@ -313,7 +314,8 @@ public void ContractsView_ContractNotFound_BackButton_NavigatesToContractsList() // Find and click the Back to List button IRefreshableElementCollection buttons = cut.FindAll("button"); IElement? backButton = buttons.FirstOrDefault(b => b.TextContent.Contains("Back to List")); - backButton?.Click(); + backButton.ShouldNotBeNull(); + backButton.Click(); // Assert _fakeNavigationManager.Uri.ShouldContain("/contracts"); From 8165127ace3ff469f2a4030a660b5897a659fcc1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:00:25 +0000 Subject: [PATCH 5/5] Fix build errors - add AngleSharp using and fix model types Co-authored-by: StuartFerguson <16325469+StuartFerguson@users.noreply.github.com> --- .../Pages/Contracts/ContractsViewPageTests.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs index d4228f01..1888eab5 100644 --- a/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsViewPageTests.cs @@ -1,7 +1,7 @@ +using AngleSharp.Dom; using Bunit; using EstateManagementUI.BlazorServer.Components.Pages.Contracts; using EstateManagementUI.BlazorServer.Components.Permissions; -using EstateManagementUI.BlazorServer.Models; using EstateManagementUI.BlazorServer.Permissions; using EstateManagementUI.BlazorServer.Tests.Pages.FileProcessing; using EstateManagementUI.BusinessLogic.Models; @@ -152,7 +152,7 @@ public void ContractsView_DisplaysProducts_WhenPresent() ContractProductId = Guid.NewGuid(), ProductName = "Test Product", DisplayText = "Test Display", - ProductType = ProductType.MobileTopup, + ProductType = "MobileTopup", Value = "100", NumberOfFees = 2 } @@ -215,7 +215,7 @@ public void ContractsView_DisplaysTransactionFees_WhenPresent() ContractProductId = Guid.NewGuid(), ProductName = "Test Product", DisplayText = "Test Display", - ProductType = ProductType.MobileTopup, + ProductType = "MobileTopup", Value = "100", NumberOfFees = 1, TransactionFees = new List @@ -224,8 +224,8 @@ public void ContractsView_DisplaysTransactionFees_WhenPresent() { TransactionFeeId = Guid.NewGuid(), Description = "Service Fee", - CalculationType = CalculationType.Fixed, - FeeType = FeeType.ServiceProvider, + CalculationType = 0, + FeeType = 0, Value = 10.50m } }