diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsNewPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsNewPageTests.cs new file mode 100644 index 00000000..9e836983 --- /dev/null +++ b/EstateManagementUI.BlazorServer.Tests/Pages/Contracts/ContractsNewPageTests.cs @@ -0,0 +1,269 @@ +using AngleSharp.Dom; +using Bunit; +using EstateManagementUI.BusinessLogic.Models; +using EstateManagementUI.BusinessLogic.Requests; +using Moq; +using Shouldly; +using SimpleResults; +using ContractsNew = EstateManagementUI.BlazorServer.Components.Pages.Contracts.New; + +namespace EstateManagementUI.BlazorServer.Tests.Pages.Contracts; + +public class ContractsNewPageTests : BaseTest +{ + [Fact] + public void ContractsNew_RendersCorrectly() + { + // Arrange + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(new List())); + + // Act + IRenderedComponent cut = RenderComponent(); + + // Assert + cut.Markup.ShouldContain("Create New Contract"); + } + + [Fact] + public void ContractsNew_HasCorrectPageTitle() + { + // Arrange + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(new List())); + + // Act + IRenderedComponent cut = RenderComponent(); + + // Assert + IRenderedComponent pageTitle = cut.FindComponent(); + pageTitle.Instance.ChildContent.ShouldNotBeNull(); + } + + [Fact] + public void ContractsNew_CancelButton_NavigatesToContractsIndex() + { + // Arrange + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(new List())); + + IRenderedComponent cut = RenderComponent(); + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Create New Contract"), timeout: TimeSpan.FromSeconds(5)); + + // Act - Find and click the Cancel button + IRefreshableElementCollection buttons = cut.FindAll("button"); + IElement? cancelButton = buttons.FirstOrDefault(b => b.TextContent.Contains("Cancel")); + cancelButton.ShouldNotBeNull(); + cancelButton.Click(); + + // Assert + _fakeNavigationManager.Uri.ShouldContain("/contracts"); + } + + [Fact] + public void ContractsNew_CreateContractButton_IsPresent() + { + // Arrange + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(new List())); + + IRenderedComponent cut = RenderComponent(); + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Create New Contract"), timeout: TimeSpan.FromSeconds(5)); + + // Act & Assert + IElement createButton = cut.Find("#createContractButton"); + createButton.ShouldNotBeNull(); + } + + [Fact] + public void ContractsNew_LoadsOperatorsSuccessfully() + { + // Arrange + List operators = new List + { + new OperatorDropDownModel + { + OperatorId = Guid.NewGuid(), + OperatorName = "Test Operator 1" + }, + new OperatorDropDownModel + { + OperatorId = Guid.NewGuid(), + OperatorName = "Test Operator 2" + } + }; + + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(operators)); + + // Act + IRenderedComponent cut = RenderComponent(); + + // Assert - Verify both operators are loaded and displayed in the dropdown + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Test Operator 1"), timeout: TimeSpan.FromSeconds(5)); + cut.Markup.ShouldContain("Test Operator 2"); + } + + [Fact] + public void ContractsNew_WithNoOperators_ShowsNoOperatorsMessage() + { + // Arrange + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(new List())); + + // Act + IRenderedComponent cut = RenderComponent(); + + // Assert + cut.WaitForAssertion(() => cut.Markup.ShouldContain("No operators available"), timeout: TimeSpan.FromSeconds(5)); + } + + [Fact] + public void ContractsNew_SuccessfulCreation_NavigatesToContractsIndex() + { + // Arrange + Guid operatorId = Guid.NewGuid(); + List operators = new List + { + new OperatorDropDownModel + { + OperatorId = operatorId, + OperatorName = "Test Operator" + } + }; + + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(operators)); + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success()); + + IRenderedComponent cut = RenderComponent(); + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Test Operator"), timeout: TimeSpan.FromSeconds(5)); + + // Act - Fill in the form + IElement descriptionInput = cut.Find("input[placeholder='Enter contract description']"); + descriptionInput.Change("Test Contract Description"); + + IElement operatorSelect = cut.Find("select"); + operatorSelect.Change(operatorId.ToString()); + + // Submit the form + IElement createButton = cut.Find("#createContractButton"); + createButton.Click(); + + // Assert + cut.WaitForAssertion(() => { + _mockMediator.Verify(x => x.Send(It.IsAny(), default), Times.Once()); + _fakeNavigationManager.Uri.ShouldContain("/contracts"); + }, timeout: TimeSpan.FromSeconds(5)); + } + + [Fact] + public void ContractsNew_FailedCreation_ShowsErrorMessage() + { + // Arrange + Guid operatorId = Guid.NewGuid(); + List operators = new List + { + new OperatorDropDownModel + { + OperatorId = operatorId, + OperatorName = "Test Operator" + } + }; + + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(operators)); + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Failure("Failed to create contract")); + + IRenderedComponent cut = RenderComponent(); + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Test Operator"), timeout: TimeSpan.FromSeconds(5)); + + // Act - Fill in the form + IElement descriptionInput = cut.Find("input[placeholder='Enter contract description']"); + descriptionInput.Change("Test Contract Description"); + + IElement operatorSelect = cut.Find("select"); + operatorSelect.Change(operatorId.ToString()); + + // Submit the form + IElement createButton = cut.Find("#createContractButton"); + createButton.Click(); + + // Assert + cut.WaitForAssertion(() => { + _mockMediator.Verify(x => x.Send(It.IsAny(), default), Times.Once()); + cut.Markup.ShouldContain("Failed to create contract"); + }, timeout: TimeSpan.FromSeconds(5)); + } + + [Fact] + public void ContractsNew_FormValidation_RequiresDescription() + { + // Arrange + Guid operatorId = Guid.NewGuid(); + List operators = new List + { + new OperatorDropDownModel + { + OperatorId = operatorId, + OperatorName = "Test Operator" + } + }; + + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(operators)); + + IRenderedComponent cut = RenderComponent(); + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Test Operator"), timeout: TimeSpan.FromSeconds(5)); + + // Act - Submit form without description + IElement operatorSelect = cut.Find("select"); + operatorSelect.Change(operatorId.ToString()); + + IElement createButton = cut.Find("#createContractButton"); + createButton.Click(); + + // Assert - Validation message should appear + cut.WaitForAssertion(() => { + cut.Markup.ShouldContain("Description is required"); + // Should not call the mediator + _mockMediator.Verify(x => x.Send(It.IsAny(), default), Times.Never()); + }, timeout: TimeSpan.FromSeconds(5)); + } + + [Fact] + public void ContractsNew_FormValidation_RequiresOperator() + { + // Arrange + List operators = new List + { + new OperatorDropDownModel + { + OperatorId = Guid.NewGuid(), + OperatorName = "Test Operator" + } + }; + + _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + .ReturnsAsync(Result.Success(operators)); + + IRenderedComponent cut = RenderComponent(); + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Test Operator"), timeout: TimeSpan.FromSeconds(5)); + + // Act - Submit form without selecting operator + IElement descriptionInput = cut.Find("input[placeholder='Enter contract description']"); + descriptionInput.Change("Test Contract Description"); + + IElement createButton = cut.Find("#createContractButton"); + createButton.Click(); + + // Assert - Validation message should appear + cut.WaitForAssertion(() => { + cut.Markup.ShouldContain("Operator is required"); + // Should not call the mediator + _mockMediator.Verify(x => x.Send(It.IsAny(), default), Times.Never()); + }, timeout: TimeSpan.FromSeconds(5)); + } +}