diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsNewPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsNewPageTests.cs new file mode 100644 index 00000000..bb3c6efe --- /dev/null +++ b/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsNewPageTests.cs @@ -0,0 +1,323 @@ +using Bunit; +using EstateManagementUI.BlazorServer.Models; +using EstateManagementUI.BusinessLogic.Requests; +using Moq; +using Shouldly; +using SimpleResults; +using MerchantsNew = EstateManagementUI.BlazorServer.Components.Pages.Merchants.New; +using AngleSharp.Dom; + +namespace EstateManagementUI.BlazorServer.Tests.Pages.Merchants; + +public class MerchantsNewPageTests : BaseTest +{ + [Fact] + public void MerchantsNew_RendersCorrectly() + { + // Act + var cut = RenderComponent(); + + // Assert + cut.Markup.ShouldContain("Create New Merchant"); + } + + [Fact] + public void MerchantsNew_HasCorrectPageTitle() + { + // Act + var cut = RenderComponent(); + + // Assert + var pageTitle = cut.FindComponent(); + pageTitle.Instance.ChildContent.ShouldNotBeNull(); + } + + [Fact] + public void MerchantsNew_CancelButton_NavigatesToMerchantsList() + { + // Arrange + var cut = RenderComponent(); + + // Act - Find and click the Cancel button + var buttons = cut.FindAll("button"); + var cancelButton = buttons.FirstOrDefault(b => b.TextContent.Contains("Cancel")); + cancelButton?.Click(); + + // Assert + _fakeNavigationManager.Uri.ShouldContain("/merchants"); + } + + [Fact] + public void MerchantsNew_CreateMerchantButton_Exists() + { + // Arrange + var cut = RenderComponent(); + + // Act - Find the Create Merchant button by ID + var createButton = cut.Find("#createMerchantButton"); + + // Assert + createButton.ShouldNotBeNull(); + createButton.TextContent.ShouldContain("Create Merchant"); + } + + [Fact(Skip = "Form submission tests require CountrySelector interaction - tracked in separate issue")] + public void MerchantsNew_SuccessfulCreation_ShowsSuccessMessage() + { + // Arrange + this.MerchantUIService.Setup(m => m.CreateMerchant( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Success); + + var cut = RenderComponent(); + cut.Instance.SetDelayOverride(0); + cut.Render(); // required to trigger re-render + + // Act - Fill in form and submit + var merchantNameInput = cut.Find("input[name='MerchantName']"); + merchantNameInput.Change("Test Merchant"); + + var settlementScheduleSelect = cut.Find("select[name='SettlementSchedule']"); + settlementScheduleSelect.Change("Weekly"); + + var addressLine1Input = cut.Find("input[name='AddressLine1']"); + addressLine1Input.Change("123 Test Street"); + + var townInput = cut.Find("input[name='Town']"); + townInput.Change("Test Town"); + + var regionInput = cut.Find("input[name='Region']"); + regionInput.Change("Test Region"); + + var postCodeInput = cut.Find("input[name='PostCode']"); + postCodeInput.Change("12345"); + + var contactNameInput = cut.Find("input[name='ContactName']"); + contactNameInput.Change("John Doe"); + + var emailInput = cut.Find("input[name='EmailAddress']"); + emailInput.Change("john@example.com"); + + var phoneInput = cut.Find("input[name='PhoneNumber']"); + phoneInput.Change("1234567890"); + + var createButton = cut.Find("#createMerchantButton"); + createButton.Click(); + + // Assert + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Merchant created successfully"), timeout: TimeSpan.FromSeconds(5)); + } + + [Fact(Skip = "Form submission tests require CountrySelector interaction - tracked in separate issue")] + public void MerchantsNew_SuccessfulCreation_NavigatesToMerchantsList() + { + // Arrange + this.MerchantUIService.Setup(m => m.CreateMerchant( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Success); + + var cut = RenderComponent(); + cut.Instance.SetDelayOverride(0); + cut.Render(); // required to trigger re-render + + // Act - Fill in form and submit + var merchantNameInput = cut.Find("input[name='MerchantName']"); + merchantNameInput.Change("Test Merchant"); + + var settlementScheduleSelect = cut.Find("select[name='SettlementSchedule']"); + settlementScheduleSelect.Change("Weekly"); + + var addressLine1Input = cut.Find("input[name='AddressLine1']"); + addressLine1Input.Change("123 Test Street"); + + var townInput = cut.Find("input[name='Town']"); + townInput.Change("Test Town"); + + var regionInput = cut.Find("input[name='Region']"); + regionInput.Change("Test Region"); + + var postCodeInput = cut.Find("input[name='PostCode']"); + postCodeInput.Change("12345"); + + var contactNameInput = cut.Find("input[name='ContactName']"); + contactNameInput.Change("John Doe"); + + var emailInput = cut.Find("input[name='EmailAddress']"); + emailInput.Change("john@example.com"); + + var phoneInput = cut.Find("input[name='PhoneNumber']"); + phoneInput.Change("1234567890"); + + var createButton = cut.Find("#createMerchantButton"); + createButton.Click(); + + // Assert - Wait for navigation + cut.WaitForAssertion(() => _fakeNavigationManager.Uri.ShouldContain("/merchants"), timeout: TimeSpan.FromSeconds(5)); + } + + [Fact(Skip = "Form submission tests require CountrySelector interaction - tracked in separate issue")] + public void MerchantsNew_FailedCreation_ShowsErrorMessage() + { + // Arrange + this.MerchantUIService.Setup(m => m.CreateMerchant( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Failure); + + var cut = RenderComponent(); + + // Act - Fill in form and submit + var merchantNameInput = cut.Find("input[name='MerchantName']"); + merchantNameInput.Change("Test Merchant"); + + var settlementScheduleSelect = cut.Find("select[name='SettlementSchedule']"); + settlementScheduleSelect.Change("Weekly"); + + var addressLine1Input = cut.Find("input[name='AddressLine1']"); + addressLine1Input.Change("123 Test Street"); + + var townInput = cut.Find("input[name='Town']"); + townInput.Change("Test Town"); + + var regionInput = cut.Find("input[name='Region']"); + regionInput.Change("Test Region"); + + var postCodeInput = cut.Find("input[name='PostCode']"); + postCodeInput.Change("12345"); + + var contactNameInput = cut.Find("input[name='ContactName']"); + contactNameInput.Change("John Doe"); + + var emailInput = cut.Find("input[name='EmailAddress']"); + emailInput.Change("john@example.com"); + + var phoneInput = cut.Find("input[name='PhoneNumber']"); + phoneInput.Change("1234567890"); + + var createButton = cut.Find("#createMerchantButton"); + createButton.Click(); + + // Assert + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Failed to create merchant"), timeout: TimeSpan.FromSeconds(5)); + } + + [Fact(Skip = "Form submission tests require CountrySelector interaction - tracked in separate issue")] + public void MerchantsNew_FailedCreation_DoesNotNavigate() + { + // Arrange + this.MerchantUIService.Setup(m => m.CreateMerchant( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Failure); + + var cut = RenderComponent(); + + // Act - Fill in form and submit + var merchantNameInput = cut.Find("input[name='MerchantName']"); + merchantNameInput.Change("Test Merchant"); + + var settlementScheduleSelect = cut.Find("select[name='SettlementSchedule']"); + settlementScheduleSelect.Change("Weekly"); + + var addressLine1Input = cut.Find("input[name='AddressLine1']"); + addressLine1Input.Change("123 Test Street"); + + var townInput = cut.Find("input[name='Town']"); + townInput.Change("Test Town"); + + var regionInput = cut.Find("input[name='Region']"); + regionInput.Change("Test Region"); + + var postCodeInput = cut.Find("input[name='PostCode']"); + postCodeInput.Change("12345"); + + var contactNameInput = cut.Find("input[name='ContactName']"); + contactNameInput.Change("John Doe"); + + var emailInput = cut.Find("input[name='EmailAddress']"); + emailInput.Change("john@example.com"); + + var phoneInput = cut.Find("input[name='PhoneNumber']"); + phoneInput.Change("1234567890"); + + var createButton = cut.Find("#createMerchantButton"); + createButton.Click(); + + // Assert - Should not navigate to /merchants + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Failed to create merchant"), timeout: TimeSpan.FromSeconds(5)); + _fakeNavigationManager.Uri.ShouldNotContain("/merchants"); + } + + [Fact(Skip = "Form submission tests require CountrySelector interaction - tracked in separate issue")] + public void MerchantsNew_SavingState_ShowsLoadingIndicator() + { + // Arrange + var tcs = new TaskCompletionSource(); + this.MerchantUIService.Setup(m => m.CreateMerchant( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns(tcs.Task); + + var cut = RenderComponent(); + + // Act - Fill in form and submit + var merchantNameInput = cut.Find("input[name='MerchantName']"); + merchantNameInput.Change("Test Merchant"); + + var settlementScheduleSelect = cut.Find("select[name='SettlementSchedule']"); + settlementScheduleSelect.Change("Weekly"); + + var addressLine1Input = cut.Find("input[name='AddressLine1']"); + addressLine1Input.Change("123 Test Street"); + + var townInput = cut.Find("input[name='Town']"); + townInput.Change("Test Town"); + + var regionInput = cut.Find("input[name='Region']"); + regionInput.Change("Test Region"); + + var postCodeInput = cut.Find("input[name='PostCode']"); + postCodeInput.Change("12345"); + + // Interact with CountrySelector - find the button that opens the dropdown + var countryButtons = cut.FindAll("button[aria-label='Select country']"); + if (countryButtons.Any()) + { + countryButtons.First().Click(); + // Find and click a country option (e.g., United Kingdom) + var countryOptions = cut.FindAll("button"); + var ukButton = countryOptions.FirstOrDefault(b => b.TextContent.Contains("United Kingdom")); + ukButton?.Click(); + } + + var contactNameInput = cut.Find("input[name='ContactName']"); + contactNameInput.Change("John Doe"); + + var emailInput = cut.Find("input[name='EmailAddress']"); + emailInput.Change("john@example.com"); + + var phoneInput = cut.Find("input[name='PhoneNumber']"); + phoneInput.Change("1234567890"); + + var createButton = cut.Find("#createMerchantButton"); + createButton.Click(); + + // Assert - Should show "Saving..." text + cut.WaitForAssertion(() => cut.Markup.ShouldContain("Saving..."), timeout: TimeSpan.FromSeconds(5)); + + // Complete the task + tcs.SetResult(Result.Success()); + } +}