diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/TestData.cs b/TransactionMobile.Maui.BusinessLogic.Tests/TestData.cs index 96f14578..ad5927ac 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/TestData.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/TestData.cs @@ -191,6 +191,56 @@ public static class TestData RequireApplicationUpdate = false, ResponseMessage = "FAILED" }; + + public static MerchantDetailsModel MerchantDetailsModel => new MerchantDetailsModel { + MerchantName = TestData.MerchantName, + Contact = new ContactModel { + Name = TestData.ContactName, + EmailAddress = TestData.ContactEmailAddress, + MobileNumber = TestData.ContactMobileNumber + }, + Address = new AddressModel { + AddressLine2 = TestData.AddressLine2, + Town = TestData.Town, + AddressLine4 = TestData.AddressLine4, + PostalCode = TestData.PostalCode, + Region = TestData.Region, + AddressLine3 = TestData.AddressLine3, + AddressLine1 = TestData.AddressLine1 + }, + AvailableBalance = TestData.AvailableBalance, + Balance = TestData.Balance, + LastStatementDate = TestData.LastStatementDate, + NextStatementDate = TestData.NextStatementDate, + SettlementSchedule = TestData.SettlementSchedule + }; + + public static Decimal Balance = 100.00m; + + public static Decimal AvailableBalance = 99.00m; + + public static String MerchantName = "Test Merchant"; + + public static DateTime NextStatementDate = new DateTime(2022, 09, 01); + public static DateTime LastStatementDate = new DateTime(2022, 08, 01); + + public static String SettlementSchedule = "Monthly"; + + // Address + public static String AddressLine1 = "Address Line 1"; + public static String AddressLine2 = "Address Line 2"; + public static String AddressLine3 = "Address Line 3"; + public static String AddressLine4 = "Address Line 4"; + public static String PostalCode = "TE57 1NG"; + public static String Region = "Region"; + public static String Town = "Town"; + + // Contact + public static String ContactEmailAddress = "testcontact@myemail.com"; + + public static String ContactName = "Mr Test Contact"; + + public static String ContactMobileNumber = "077777777"; } } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccount/MyAccountAddressPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccount/MyAccountAddressPageViewModelTests.cs new file mode 100644 index 00000000..29d3e3a0 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccount/MyAccountAddressPageViewModelTests.cs @@ -0,0 +1,40 @@ +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.MyAccount; + +using System.Threading; +using System.Threading.Tasks; +using Maui.UIServices; +using MediatR; +using Moq; +using Services; +using Shared.Logger; +using Shouldly; +using ViewModels.MyAccount; +using Xunit; + +public class MyAccountAddressPageViewModelTests +{ + [Fact] + public async Task MyAccountAddressPageViewModel_Initialise_IsInitialised() + { + Logger.Initialise(NullLogger.Instance); + Mock navigationService = new Mock(); + Mock applicationCache = new Mock(); + applicationCache.Setup(a => a.GetMerchantDetails()).Returns(TestData.MerchantDetailsModel); + Mock mediator = new Mock(); + + MyAccountAddressPageViewModel viewModel = new MyAccountAddressPageViewModel(navigationService.Object, + applicationCache.Object, + mediator.Object); + await viewModel.Initialise(CancellationToken.None); + + applicationCache.Verify(a => a.GetMerchantDetails(), Times.Once); + viewModel.Address.ShouldNotBeNull(); + viewModel.Address.AddressLine1.ShouldBe(TestData.AddressLine1); + viewModel.Address.AddressLine2.ShouldBe(TestData.AddressLine2); + viewModel.Address.AddressLine3.ShouldBe(TestData.AddressLine3); + viewModel.Address.AddressLine4.ShouldBe(TestData.AddressLine4); + viewModel.Address.Region.ShouldBe(TestData.Region); + viewModel.Address.Town.ShouldBe(TestData.Town); + viewModel.Address.PostalCode.ShouldBe(TestData.PostalCode); + } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccount/MyAccountContactPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccount/MyAccountContactPageViewModelTests.cs new file mode 100644 index 00000000..f3a0f774 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccount/MyAccountContactPageViewModelTests.cs @@ -0,0 +1,36 @@ +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.MyAccount; + +using System.Threading; +using System.Threading.Tasks; +using Maui.UIServices; +using MediatR; +using Moq; +using Services; +using Shared.Logger; +using Shouldly; +using ViewModels.MyAccount; +using Xunit; + +public class MyAccountContactPageViewModelTests +{ + [Fact] + public async Task MyAccountContactPageViewModel_Initialise_IsInitialised() + { + Logger.Initialise(NullLogger.Instance); + Mock navigationService = new Mock(); + Mock applicationCache = new Mock(); + applicationCache.Setup(a => a.GetMerchantDetails()).Returns(TestData.MerchantDetailsModel); + Mock mediator = new Mock(); + + MyAccountContactPageViewModel viewModel = new MyAccountContactPageViewModel(navigationService.Object, + applicationCache.Object, + mediator.Object); + await viewModel.Initialise(CancellationToken.None); + + applicationCache.Verify(a => a.GetMerchantDetails(), Times.Once); + viewModel.Contact.ShouldNotBeNull(); + viewModel.Contact.EmailAddress.ShouldBe(TestData.ContactEmailAddress); + viewModel.Contact.Name.ShouldBe(TestData.ContactName); + viewModel.Contact.MobileNumber.ShouldBe(TestData.ContactMobileNumber); + } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccount/MyAccountDetailsPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccount/MyAccountDetailsPageViewModelTests.cs new file mode 100644 index 00000000..d35b5e29 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccount/MyAccountDetailsPageViewModelTests.cs @@ -0,0 +1,38 @@ +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.MyAccount; + +using System.Threading; +using System.Threading.Tasks; +using Maui.UIServices; +using MediatR; +using Moq; +using Services; +using Shared.Logger; +using Shouldly; +using ViewModels.MyAccount; +using Xunit; + +public class MyAccountDetailsPageViewModelTests +{ + [Fact] + public async Task MyAccountDetailsPageViewModel_Initialise_IsInitialised() + { + Logger.Initialise(NullLogger.Instance); + Mock navigationService = new Mock(); + Mock applicationCache = new Mock(); + applicationCache.Setup(a => a.GetMerchantDetails()).Returns(TestData.MerchantDetailsModel); + Mock mediator = new Mock(); + + MyAccountDetailsPageViewModel viewModel = new MyAccountDetailsPageViewModel(navigationService.Object, + applicationCache.Object, + mediator.Object); + await viewModel.Initialise(CancellationToken.None); + + applicationCache.Verify(a => a.GetMerchantDetails(), Times.Once); + viewModel.Balance.ShouldBe(TestData.Balance); + viewModel.AvailableBalance.ShouldBe(TestData.AvailableBalance); + viewModel.MerchantName.ShouldBe(TestData.MerchantName); + viewModel.LastStatementDate.ShouldBe(TestData.LastStatementDate); + viewModel.NextStatementDate.ShouldBe(TestData.NextStatementDate); + viewModel.SettlementSchedule.ShouldBe(TestData.SettlementSchedule); + } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccount/MyAccountPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccount/MyAccountPageViewModelTests.cs new file mode 100644 index 00000000..ac94bf26 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccount/MyAccountPageViewModelTests.cs @@ -0,0 +1,97 @@ +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.MyAccount; + +using System; +using System.Threading; +using System.Threading.Tasks; +using Common; +using Maui.UIServices; +using MediatR; +using Microsoft.Extensions.Caching.Memory; +using Models; +using Moq; +using Requests; +using Services; +using Shared.Logger; +using Shouldly; +using ViewModels.MyAccount; +using Xunit; + +public class MyAccountPageViewModelTests +{ + #region Methods + + [Fact] + public async Task MyAccountPageViewModel_Initialise_IsInitialised() { + Logger.Initialise(NullLogger.Instance); + Mock navigationService = new Mock(); + Mock applicationCache = new Mock(); + Mock mediator = new Mock(); + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.MerchantDetailsModel); + + MyAccountPageViewModel viewModel = new MyAccountPageViewModel(navigationService.Object, applicationCache.Object, mediator.Object); + await viewModel.Initialise(CancellationToken.None); + + viewModel.MerchantName.ShouldBe(TestData.MerchantDetailsModel.MerchantName); + viewModel.LastLogin.ShouldBe(DateTime.Now, TimeSpan.FromSeconds(30)); + applicationCache.Verify(a => a.SetMerchantDetails(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public void MyAccountPageViewModel_OptionSelectedCommand_AccountInfo_Execute_IsExecuted() { + Logger.Initialise(NullLogger.Instance); + Mock navigationService = new Mock(); + Mock applicationCache = new Mock(); + Mock mediator = new Mock(); + MyAccountPageViewModel viewModel = new MyAccountPageViewModel(navigationService.Object, applicationCache.Object, mediator.Object); + viewModel.OptionSelectedCommand.Execute(this.CreateItemSelected(MyAccountPageViewModel.AccountOptions.AccountInfo)); + + navigationService.Verify(n => n.GoToMyAccountDetails(), Times.Once); + } + + [Fact] + public void MyAccountPageViewModel_OptionSelectedCommand_Addresses_Execute_IsExecuted() { + Logger.Initialise(NullLogger.Instance); + Mock navigationService = new Mock(); + Mock applicationCache = new Mock(); + Mock mediator = new Mock(); + + MyAccountPageViewModel viewModel = new MyAccountPageViewModel(navigationService.Object, applicationCache.Object, mediator.Object); + viewModel.OptionSelectedCommand.Execute(this.CreateItemSelected(MyAccountPageViewModel.AccountOptions.Addresses)); + + navigationService.Verify(n => n.GoToMyAccountAddresses(), Times.Once); + } + + [Fact] + public void MyAccountPageViewModel_OptionSelectedCommand_Contacts_Execute_IsExecuted() { + Logger.Initialise(NullLogger.Instance); + Mock navigationService = new Mock(); + Mock applicationCache = new Mock(); + Mock mediator = new Mock(); + + MyAccountPageViewModel viewModel = new MyAccountPageViewModel(navigationService.Object, applicationCache.Object, mediator.Object); + viewModel.OptionSelectedCommand.Execute(this.CreateItemSelected(MyAccountPageViewModel.AccountOptions.Contacts)); + + navigationService.Verify(n => n.GoToMyAccountContacts(), Times.Once); + } + + [Fact] + public void MyAccountPageViewModel_OptionSelectedCommand_Logout_Execute_IsExecuted() { + Logger.Initialise(NullLogger.Instance); + Mock navigationService = new Mock(); + Mock applicationCache = new Mock(); + Mock mediator = new Mock(); + MyAccountPageViewModel viewModel = new MyAccountPageViewModel(navigationService.Object, applicationCache.Object, mediator.Object); + + viewModel.OptionSelectedCommand.Execute(this.CreateItemSelected(MyAccountPageViewModel.AccountOptions.Logout)); + + navigationService.Verify(n => n.GoToLoginPage(), Times.Once); + } + + private ItemSelected CreateItemSelected(MyAccountPageViewModel.AccountOptions selectedOption) { + ItemSelected i = new ItemSelected(); + i.SelectedItemIndex = (Int32)selectedOption; + return i; + } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccountPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccountPageViewModelTests.cs deleted file mode 100644 index a8071d1e..00000000 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MyAccountPageViewModelTests.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; - -using Maui.UIServices; -using Moq; -using Services; -using ViewModels.MyAccount; -using Xunit; - -public class MyAccountPageViewModelTests -{ - [Fact] - public void MyAccountPageViewModel_LogoutCommand_Execute_IsExecuted() - { - Mock navigationService = new Mock(); - Mock applicationCache = new Mock(); - MyAccountPageViewModel viewModel = new MyAccountPageViewModel(navigationService.Object, - applicationCache.Object); - - viewModel.LogoutCommand.Execute(null); - navigationService.Verify(n => n.GoToLoginPage(), Times.Once); - } -} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/SupportPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Support/SupportPageViewModelTests.cs similarity index 99% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/SupportPageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Support/SupportPageViewModelTests.cs index 0a976844..d61b281f 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/SupportPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Support/SupportPageViewModelTests.cs @@ -1,6 +1,6 @@ using System; -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Support { using Maui.UIServices; using MediatR; @@ -33,7 +33,7 @@ public void SupportPageViewModel_UploadLogsCommand_Execute_IsExecuted() applicationCache.Object); viewModel.UploadLogsCommand.Execute(null); - + navigationService.Verify(n => n.GoToHome(), Times.Once); } } diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/AdminPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Admin/AdminPageViewModelTests.cs similarity index 98% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/AdminPageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Admin/AdminPageViewModelTests.cs index e77d6d83..9a8b99cf 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/AdminPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Admin/AdminPageViewModelTests.cs @@ -3,7 +3,7 @@ using System.Text; using System.Threading.Tasks; -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Transactions.Admin { using Maui.UIServices; using MediatR; diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupFailedPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupFailedPageViewModelTests.cs similarity index 95% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupFailedPageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupFailedPageViewModelTests.cs index 92b8e75a..fe311e1c 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupFailedPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupFailedPageViewModelTests.cs @@ -1,4 +1,4 @@ -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Transactions.MobileTopup; using Maui.UIServices; using Moq; diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupPerformTopupPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupPerformTopupPageViewModelTests.cs similarity index 93% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupPerformTopupPageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupPerformTopupPageViewModelTests.cs index ea956e8d..5bf5bcc2 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupPerformTopupPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupPerformTopupPageViewModelTests.cs @@ -1,4 +1,4 @@ -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Transactions.MobileTopup; using System; using System.Collections.Generic; @@ -23,7 +23,7 @@ public void MobileTopupPerformTopupPageViewModel_ApplyQueryAttributes_QueryAttri Logger.Initialise(NullLogger.Instance); MobileTopupPerformTopupPageViewModel viewModel = new MobileTopupPerformTopupPageViewModel(mediator.Object, navigationService.Object); - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, @@ -44,13 +44,13 @@ public void MobileTopupPerformTopupPageViewModel_CustomerEmailAddressEntryComple Mock navigationService = new Mock(); Logger.Initialise(NullLogger.Instance); MobileTopupPerformTopupPageViewModel viewModel = new MobileTopupPerformTopupPageViewModel(mediator.Object, navigationService.Object); - Boolean isCompletedCalled = false; + bool isCompletedCalled = false; viewModel.OnCustomerEmailAddressEntryCompleted = () => { isCompletedCalled = true; }; - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, @@ -68,13 +68,13 @@ public void MobileTopupPerformTopupPageViewModel_CustomerMobileNumberEntryComple Mock navigationService = new Mock(); Logger.Initialise(NullLogger.Instance); MobileTopupPerformTopupPageViewModel viewModel = new MobileTopupPerformTopupPageViewModel(mediator.Object, navigationService.Object); - Boolean isCompletedCalled = false; + bool isCompletedCalled = false; viewModel.OnCustomerMobileNumberEntryCompleted = () => { isCompletedCalled = true; }; - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, @@ -92,13 +92,13 @@ public void MobileTopupPerformTopupPageViewModel_TopupAmountEntryCompletedComman Mock navigationService = new Mock(); Logger.Initialise(NullLogger.Instance); MobileTopupPerformTopupPageViewModel viewModel = new MobileTopupPerformTopupPageViewModel(mediator.Object, navigationService.Object); - Boolean isCompletedCalled = false; + bool isCompletedCalled = false; viewModel.OnTopupAmountEntryCompleted = () => { isCompletedCalled = true; }; - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, @@ -117,7 +117,7 @@ public void MobileTopupPerformTopupPageViewModel_PerformTopupCommand_Execute_Suc Mock navigationService = new Mock(); Logger.Initialise(NullLogger.Instance); MobileTopupPerformTopupPageViewModel viewModel = new MobileTopupPerformTopupPageViewModel(mediator.Object, navigationService.Object); - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, @@ -125,8 +125,8 @@ public void MobileTopupPerformTopupPageViewModel_PerformTopupCommand_Execute_Suc {nameof(viewModel.TopupAmount), TestData.Operator1Product_100KES.Value} }); viewModel.PerformTopupCommand.Execute(null); - mediator.Verify(m => m.Send(It.IsAny(), It.IsAny()),Times.Once); - navigationService.Verify(v => v.GoToMobileTopupSuccessPage(),Times.Once); + mediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + navigationService.Verify(v => v.GoToMobileTopupSuccessPage(), Times.Once); } [Fact] @@ -137,7 +137,7 @@ public void MobileTopupPerformTopupPageViewModel_PerformTopupCommand_Execute_Fai Mock navigationService = new Mock(); Logger.Initialise(NullLogger.Instance); MobileTopupPerformTopupPageViewModel viewModel = new MobileTopupPerformTopupPageViewModel(mediator.Object, navigationService.Object); - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSelectOperatorPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupSelectOperatorPageViewModelTests.cs similarity index 85% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSelectOperatorPageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupSelectOperatorPageViewModelTests.cs index 8b316b30..b27fa68d 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSelectOperatorPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupSelectOperatorPageViewModelTests.cs @@ -1,4 +1,4 @@ -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Transactions.MobileTopup; using System.Threading; using System.Threading.Tasks; @@ -44,10 +44,10 @@ public async Task MobileTopupSelectOperatorPageViewModel_OperatorSelectedCommand viewModel.Operators.Count.ShouldBe(3); ItemSelected selectedContractOperator = new ItemSelected - { - SelectedItemIndex = 1, - SelectedItem = TestData.ContractOperatorModel - }; + { + SelectedItemIndex = 1, + SelectedItem = TestData.ContractOperatorModel + }; viewModel.OperatorSelectedCommand.Execute(selectedContractOperator); diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSelectProductPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupSelectProductPageViewModelTests.cs similarity index 73% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSelectProductPageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupSelectProductPageViewModelTests.cs index ddb1ebbf..2d1fc48e 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSelectProductPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupSelectProductPageViewModelTests.cs @@ -1,4 +1,4 @@ -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Transactions.MobileTopup; using System; using System.Collections.Generic; @@ -12,39 +12,36 @@ namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; using Requests; using Shared.Logger; using Shouldly; -using UIServices; using ViewModels.Transactions; using Xunit; public class MobileTopupSelectProductPageViewModelTests { + #region Methods + [Fact] - public async Task MobileTopupSelectProductPageViewModel_ApplyQueryAttributes_QueryAttributesApplied() - { + public async Task MobileTopupSelectProductPageViewModel_ApplyQueryAttributes_QueryAttributesApplied() { Mock mediator = new Mock(); mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.ContractProductList); Mock navigationService = new Mock(); MobileTopupSelectProductPageViewModel viewModel = new MobileTopupSelectProductPageViewModel(mediator.Object, navigationService.Object); - viewModel.ApplyQueryAttributes(new Dictionary - { - {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} - }); + viewModel.ApplyQueryAttributes(new Dictionary { + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} + }); viewModel.OperatorIdentifier.ShouldBe(TestData.OperatorIdentifier1); } [Fact] - public async Task MobileTopupSelectProductPageViewModel_Initialise_IsInitialised() - { + public async Task MobileTopupSelectProductPageViewModel_Initialise_IsInitialised() { Mock mediator = new Mock(); mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.ContractProductList); Mock navigationService = new Mock(); MobileTopupSelectProductPageViewModel viewModel = new MobileTopupSelectProductPageViewModel(mediator.Object, navigationService.Object); - viewModel.ApplyQueryAttributes(new Dictionary - { - {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} - }); + viewModel.ApplyQueryAttributes(new Dictionary { + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} + }); await viewModel.Initialise(CancellationToken.None); mediator.Verify(x => x.Send(It.IsAny(), It.IsAny()), Times.Once); @@ -52,33 +49,30 @@ public async Task MobileTopupSelectProductPageViewModel_Initialise_IsInitialised } [Fact] - public async Task MobileTopupSelectProductPageViewModel_ProductSelectedCommand_Execute_IsExecuted() - { + public async Task MobileTopupSelectProductPageViewModel_ProductSelectedCommand_Execute_IsExecuted() { Mock mediator = new Mock(); mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.ContractProductList); Mock navigationService = new Mock(); Logger.Initialise(NullLogger.Instance); MobileTopupSelectProductPageViewModel viewModel = new MobileTopupSelectProductPageViewModel(mediator.Object, navigationService.Object); - viewModel.ApplyQueryAttributes(new Dictionary - { - {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} - }); + viewModel.ApplyQueryAttributes(new Dictionary { + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} + }); await viewModel.Initialise(CancellationToken.None); mediator.Verify(x => x.Send(It.IsAny(), It.IsAny()), Times.Once); viewModel.Products.Count.ShouldBe(3); - ItemSelected selectedContractProduct = new ItemSelected - { - SelectedItemIndex = 1, - SelectedItem = TestData.Operator1Product_100KES - }; + ItemSelected selectedContractProduct = new ItemSelected { + SelectedItemIndex = 1, + SelectedItem = TestData.Operator1Product_100KES + }; viewModel.ProductSelectedCommand.Execute(selectedContractProduct); - navigationService.Verify(n=> n.GoToMobileTopupPerformTopupPage(It.IsAny(), It.IsAny(), - It.IsAny(),It.IsAny()), Times.Once); - + navigationService.Verify(n => n.GoToMobileTopupPerformTopupPage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } + + #endregion } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSuccessPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupSuccessPageViewModelTests.cs similarity index 95% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSuccessPageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupSuccessPageViewModelTests.cs index 2ba7c465..718ef342 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSuccessPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/MobileTopup/MobileTopupSuccessPageViewModelTests.cs @@ -1,4 +1,4 @@ -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Transactions.MobileTopup; using Maui.UIServices; using Moq; diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/TransactionsPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/TransactionsPageViewModelTests.cs similarity index 99% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/TransactionsPageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/TransactionsPageViewModelTests.cs index 55f49cd8..bcff196f 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/TransactionsPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/TransactionsPageViewModelTests.cs @@ -1,4 +1,4 @@ -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Transactions; using Maui.UIServices; using Moq; @@ -14,7 +14,7 @@ public void TransactionsPageViewModel_AdminCommand_Execute_IsExecuted() { Mock navigationService = new Mock(); TransactionsPageViewModel viewModel = new TransactionsPageViewModel(navigationService.Object); - + viewModel.AdminCommand.Execute(null); navigationService.Verify(n => n.GoToAdminPage(), Times.Once); } diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueFailedPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherIssueFailedPageViewModelTests.cs similarity index 96% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueFailedPageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherIssueFailedPageViewModelTests.cs index 06aa1cd3..46d252fb 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueFailedPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherIssueFailedPageViewModelTests.cs @@ -1,4 +1,4 @@ -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Transactions.Voucher; using Maui.UIServices; using Moq; diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSelectProductPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherIssueSelectProductPageViewModelTests.cs similarity index 75% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSelectProductPageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherIssueSelectProductPageViewModelTests.cs index eb8fd3c7..7d66fb57 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSelectProductPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherIssueSelectProductPageViewModelTests.cs @@ -1,4 +1,4 @@ -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Transactions.Voucher; using System; using System.Collections.Generic; @@ -12,39 +12,36 @@ namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; using Requests; using Shared.Logger; using Shouldly; -using UIServices; using ViewModels.Transactions; using Xunit; public class VoucherIssueSelectProductPageViewModelTests { + #region Methods + [Fact] - public async Task VoucherIssueSelectProductPageViewModel_ApplyQueryAttributes_QueryAttributesApplied() - { + public async Task VoucherIssueSelectProductPageViewModel_ApplyQueryAttributes_QueryAttributesApplied() { Mock mediator = new Mock(); mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.ContractProductList); Mock navigationService = new Mock(); VoucherSelectProductPageViewModel viewModel = new VoucherSelectProductPageViewModel(mediator.Object, navigationService.Object); - viewModel.ApplyQueryAttributes(new Dictionary - { - {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} - }); + viewModel.ApplyQueryAttributes(new Dictionary { + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} + }); viewModel.OperatorIdentifier.ShouldBe(TestData.OperatorIdentifier1); } [Fact] - public async Task VoucherIssueSelectProductPageViewModel_Initialise_IsInitialised() - { + public async Task VoucherIssueSelectProductPageViewModel_Initialise_IsInitialised() { Mock mediator = new Mock(); mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.ContractProductList); Mock navigationService = new Mock(); VoucherSelectProductPageViewModel viewModel = new VoucherSelectProductPageViewModel(mediator.Object, navigationService.Object); - viewModel.ApplyQueryAttributes(new Dictionary - { - {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} - }); + viewModel.ApplyQueryAttributes(new Dictionary { + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} + }); await viewModel.Initialise(CancellationToken.None); mediator.Verify(x => x.Send(It.IsAny(), It.IsAny()), Times.Once); @@ -52,33 +49,30 @@ public async Task VoucherIssueSelectProductPageViewModel_Initialise_IsInitialise } [Fact] - public async Task VoucherIssueSelectProductPageViewModel_ProductSelectedCommand_Execute_IsExecuted() - { + public async Task VoucherIssueSelectProductPageViewModel_ProductSelectedCommand_Execute_IsExecuted() { Mock mediator = new Mock(); mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.ContractProductList); Mock navigationService = new Mock(); Logger.Initialise(NullLogger.Instance); VoucherSelectProductPageViewModel viewModel = new VoucherSelectProductPageViewModel(mediator.Object, navigationService.Object); - viewModel.ApplyQueryAttributes(new Dictionary - { - {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} - }); + viewModel.ApplyQueryAttributes(new Dictionary { + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} + }); await viewModel.Initialise(CancellationToken.None); mediator.Verify(x => x.Send(It.IsAny(), It.IsAny()), Times.Once); viewModel.Products.Count.ShouldBe(3); - ItemSelected selectedContractProduct = new ItemSelected - { - SelectedItemIndex = 1, - SelectedItem = TestData.Operator1Product_100KES - }; + ItemSelected selectedContractProduct = new ItemSelected { + SelectedItemIndex = 1, + SelectedItem = TestData.Operator1Product_100KES + }; viewModel.ProductSelectedCommand.Execute(selectedContractProduct); - navigationService.Verify(n => n.GoToVoucherIssueVoucherPage(It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny()), Times.Once); - + navigationService.Verify(n => n.GoToVoucherIssueVoucherPage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } + + #endregion } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSuccessPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherIssueSuccessPageViewModelTests.cs similarity index 96% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSuccessPageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherIssueSuccessPageViewModelTests.cs index 3b2515d1..7337b99c 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSuccessPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherIssueSuccessPageViewModelTests.cs @@ -1,4 +1,4 @@ -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Transactions.Voucher; using Maui.UIServices; using Moq; diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherPerformIssuePageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherPerformIssuePageViewModelTests.cs similarity index 94% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherPerformIssuePageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherPerformIssuePageViewModelTests.cs index 07623395..3390bdbd 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherPerformIssuePageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherPerformIssuePageViewModelTests.cs @@ -1,4 +1,4 @@ -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Transactions.Voucher; using System; using System.Collections.Generic; @@ -22,7 +22,7 @@ public void VoucherPerformIssuePageViewModel_ApplyQueryAttributes_QueryAttribute Mock navigationService = new Mock(); VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, @@ -43,13 +43,13 @@ public void VoucherPerformIssuePageViewModel_CustomerEmailAddressEntryCompletedC Mock navigationService = new Mock(); Logger.Initialise(NullLogger.Instance); VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); - Boolean isCompletedCalled = false; + bool isCompletedCalled = false; viewModel.OnCustomerEmailAddressEntryCompleted = () => { isCompletedCalled = true; }; - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, @@ -67,13 +67,13 @@ public void VoucherPerformIssuePageViewModel_RecipientMobileNumberEntryCompleted Mock navigationService = new Mock(); Logger.Initialise(NullLogger.Instance); VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); - Boolean isCompletedCalled = false; + bool isCompletedCalled = false; viewModel.OnRecipientMobileNumberEntryCompleted = () => { isCompletedCalled = true; }; - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, @@ -91,13 +91,13 @@ public void VoucherPerformIssuePageViewModel_RecipientEmailAddressEntryCompleted Mock navigationService = new Mock(); Logger.Initialise(NullLogger.Instance); VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); - Boolean isCompletedCalled = false; + bool isCompletedCalled = false; viewModel.OnRecipientEmailAddressEntryCompleted = () => { isCompletedCalled = true; }; - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, @@ -115,13 +115,13 @@ public void VoucherPerformIssuePageViewModel_VoucherAmountEntryCompletedCommand_ Mock navigationService = new Mock(); Logger.Initialise(NullLogger.Instance); VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); - Boolean isCompletedCalled = false; + bool isCompletedCalled = false; viewModel.OnVoucherAmountEntryCompleted = () => { isCompletedCalled = true; }; - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, @@ -140,7 +140,7 @@ public void VoucherPerformIssuePageViewModel_IssueVoucherCommand_Execute_Success Mock navigationService = new Mock(); Logger.Initialise(NullLogger.Instance); VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, @@ -161,7 +161,7 @@ public void VoucherPerformIssuePageViewModel_IssueVoucherCommand_Execute_FailedV Logger.Initialise(NullLogger.Instance); VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); - viewModel.ApplyQueryAttributes(new Dictionary + viewModel.ApplyQueryAttributes(new Dictionary { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherSelectOperatorPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherSelectOperatorPageViewModelTests.cs similarity index 85% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherSelectOperatorPageViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherSelectOperatorPageViewModelTests.cs index 0141eb0e..7050c5ff 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherSelectOperatorPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/Transactions/Voucher/VoucherSelectOperatorPageViewModelTests.cs @@ -1,4 +1,4 @@ -namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests.Transactions.Voucher; using System.Threading; using System.Threading.Tasks; @@ -44,10 +44,10 @@ public async Task VoucherSelectOperatorPageViewModel_OperatorSelectedCommand_Exe viewModel.Operators.Count.ShouldBe(3); ItemSelected selectedContractOperator = new ItemSelected - { - SelectedItemIndex = 1, - SelectedItem = TestData.ContractOperatorModel - }; + { + SelectedItemIndex = 1, + SelectedItem = TestData.ContractOperatorModel + }; viewModel.OperatorSelectedCommand.Execute(selectedContractOperator); diff --git a/TransactionMobile.Maui.BusinessLogic/Database/DatabaseContext.cs b/TransactionMobile.Maui.BusinessLogic/Database/DatabaseContext.cs index 52bb35ad..2aeca90a 100644 --- a/TransactionMobile.Maui.BusinessLogic/Database/DatabaseContext.cs +++ b/TransactionMobile.Maui.BusinessLogic/Database/DatabaseContext.cs @@ -49,9 +49,9 @@ public async Task> GetLogMessages(int batchSize) { if (this.Connection == null) return new List(); - - List messages = this.Connection.Table().OrderBy(l => l.EntryDateTime).Take(batchSize).ToList(); - + + List messages = this.Connection.Table().OrderByDescending(l => l.EntryDateTime).Take(batchSize).ToList(); + return messages; } diff --git a/TransactionMobile.Maui.BusinessLogic/Models/ContractProductModel.cs b/TransactionMobile.Maui.BusinessLogic/Models/ContractProductModel.cs index 615c1267..97bd6a08 100644 --- a/TransactionMobile.Maui.BusinessLogic/Models/ContractProductModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/Models/ContractProductModel.cs @@ -78,4 +78,34 @@ public class ContractProductModel #endregion } + + public class MerchantDetailsModel + { + public Decimal Balance { get; set; } + public Decimal AvailableBalance { get; set; } + public String MerchantName { get; set; } + public DateTime NextStatementDate { get; set; } + public DateTime LastStatementDate { get; set; } + public String SettlementSchedule { get; set; } + public AddressModel Address { get; set; } + public ContactModel Contact { get; set; } + } + + public class AddressModel + { + public String AddressLine1 { get; set; } + public String AddressLine2 { get; set; } + public String AddressLine3 { get; set; } + public String AddressLine4 { get; set; } + public String PostalCode { get; set; } + public String Region { get; set; } + public String Town { get; set; } + } + + public class ContactModel + { + public String EmailAddress { get; set; } + public String Name { get; set; } + public String MobileNumber { get; set; } + } } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/Models/ListViewItem.cs b/TransactionMobile.Maui.BusinessLogic/Models/ListViewItem.cs new file mode 100644 index 00000000..fd5bd788 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/Models/ListViewItem.cs @@ -0,0 +1,6 @@ +namespace TransactionMobile.Maui.BusinessLogic.Models; + +public class ListViewItem +{ + public String Title { get; set; } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs b/TransactionMobile.Maui.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs index 5e995215..a2972b88 100644 --- a/TransactionMobile.Maui.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs +++ b/TransactionMobile.Maui.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs @@ -1,25 +1,26 @@ namespace TransactionMobile.Maui.BusinessLogic.RequestHandlers; using MediatR; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Primitives; using Models; using Requests; using Services; -public class MerchantRequestHandler : IRequestHandler>, IRequestHandler +public class MerchantRequestHandler : IRequestHandler>, + IRequestHandler, + IRequestHandler { #region Fields - private readonly Func MerchantServiceResolver; - private readonly IApplicationCache ApplicationCache; - + + private readonly Func MerchantServiceResolver; + #endregion #region Constructors - public MerchantRequestHandler(Func merchantServiceResolver,IApplicationCache applicationCache) - { + + public MerchantRequestHandler(Func merchantServiceResolver, + IApplicationCache applicationCache) { this.MerchantServiceResolver = merchantServiceResolver; this.ApplicationCache = applicationCache; } @@ -29,19 +30,16 @@ public MerchantRequestHandler(Func merchantServiceRes #region Methods public async Task> Handle(GetContractProductsRequest request, - CancellationToken cancellationToken) - { + CancellationToken cancellationToken) { List products = this.ApplicationCache.GetContractProducts(); Boolean useTrainingMode = this.ApplicationCache.GetUseTrainingMode(); IMerchantService merchantService = this.MerchantServiceResolver(useTrainingMode); - if (products == null || products.Any() == false) - { + if (products == null || products.Any() == false) { products = await merchantService.GetContractProducts(cancellationToken); } - if (request.ProductType.HasValue) - { + if (request.ProductType.HasValue) { products = products.Where(p => p.ProductType == request.ProductType).ToList(); } @@ -54,6 +52,16 @@ public async Task Handle(GetMerchantBalanceRequest request, IMerchantService merchantService = this.MerchantServiceResolver(useTrainingMode); return await merchantService.GetMerchantBalance(cancellationToken); } - + + public async Task Handle(GetMerchantDetailsRequest request, + CancellationToken cancellationToken) { + Boolean useTrainingMode = this.ApplicationCache.GetUseTrainingMode(); + IMerchantService merchantService = this.MerchantServiceResolver(useTrainingMode); + + MerchantDetailsModel merchantDetails = await merchantService.GetMerchantDetails(cancellationToken); + + return merchantDetails; + } + #endregion } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/Requests/GetMerchantDetailsRequest.cs b/TransactionMobile.Maui.BusinessLogic/Requests/GetMerchantDetailsRequest.cs new file mode 100644 index 00000000..f97e9587 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/Requests/GetMerchantDetailsRequest.cs @@ -0,0 +1,19 @@ +namespace TransactionMobile.Maui.BusinessLogic.Requests; + +using MediatR; +using Models; + +public class GetMerchantDetailsRequest : IRequest +{ + #region Constructors + + #endregion + + #region Methods + + public static GetMerchantDetailsRequest Create() { + return new GetMerchantDetailsRequest(); + } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/Services/ApplicationCache.cs b/TransactionMobile.Maui.BusinessLogic/Services/ApplicationCache.cs index 9b03ab83..5bf68d37 100644 --- a/TransactionMobile.Maui.BusinessLogic/Services/ApplicationCache.cs +++ b/TransactionMobile.Maui.BusinessLogic/Services/ApplicationCache.cs @@ -79,6 +79,15 @@ public void SetMerchantId(Guid value, this.Set("MerchantId", value, options); } + public MerchantDetailsModel GetMerchantDetails() { + return this.TryGetValue("MerchantDetails"); + } + + public void SetMerchantDetails(MerchantDetailsModel value, + MemoryCacheEntryOptions options = default) { + this.Set("MerchantDetails", value, options); + } + public void SetUseTrainingMode(Boolean value, MemoryCacheEntryOptions options = default) { this.Set("UseTrainingMode", value, options); diff --git a/TransactionMobile.Maui.BusinessLogic/Services/DummyServices/DummyMerchantService.cs b/TransactionMobile.Maui.BusinessLogic/Services/DummyServices/DummyMerchantService.cs index 58bfb17e..4c331de0 100644 --- a/TransactionMobile.Maui.BusinessLogic/Services/DummyServices/DummyMerchantService.cs +++ b/TransactionMobile.Maui.BusinessLogic/Services/DummyServices/DummyMerchantService.cs @@ -96,4 +96,30 @@ public async Task GetMerchantBalance(CancellationToken cancellationToke { return 100; } + + public async Task GetMerchantDetails(CancellationToken cancellationToken) { + MerchantDetailsModel model = new MerchantDetailsModel { + Address = new AddressModel { + AddressLine1 = "test address line 1", + AddressLine2 = "test address line 2", + AddressLine3 = "test address line 3", + AddressLine4 = "test address line 4", + PostalCode = "TE57 1NG", + Region = "Region", + Town = "Town" + }, + Contact = new ContactModel { + Name = "Test Contact", + EmailAddress = "stuart_ferguson1@outlook.com", + MobileNumber = "123456789" + }, + LastStatementDate = new DateTime(2022,8,1), + NextStatementDate = new DateTime(2022, 9, 1), + MerchantName = "Dummy Merchant", + SettlementSchedule = "Monthly", + AvailableBalance = 100, + Balance = 99 + }; + return model; + } } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/Services/IApplicationCache.cs b/TransactionMobile.Maui.BusinessLogic/Services/IApplicationCache.cs index 72bc56d0..4d5ce0c4 100644 --- a/TransactionMobile.Maui.BusinessLogic/Services/IApplicationCache.cs +++ b/TransactionMobile.Maui.BusinessLogic/Services/IApplicationCache.cs @@ -32,4 +32,8 @@ public interface IApplicationCache Guid GetMerchantId(); void SetMerchantId(Guid value, MemoryCacheEntryOptions options = default); + + MerchantDetailsModel GetMerchantDetails(); + + void SetMerchantDetails(MerchantDetailsModel value, MemoryCacheEntryOptions options = default); } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/Services/IMerchantService.cs b/TransactionMobile.Maui.BusinessLogic/Services/IMerchantService.cs index 4bf933d3..58b3a0ee 100644 --- a/TransactionMobile.Maui.BusinessLogic/Services/IMerchantService.cs +++ b/TransactionMobile.Maui.BusinessLogic/Services/IMerchantService.cs @@ -13,5 +13,7 @@ public interface IMerchantService Task GetMerchantBalance(CancellationToken cancellationToken); + Task GetMerchantDetails(CancellationToken cancellationToken); + #endregion } diff --git a/TransactionMobile.Maui.BusinessLogic/Services/MerchantService.cs b/TransactionMobile.Maui.BusinessLogic/Services/MerchantService.cs index 44175d68..b9a3f425 100644 --- a/TransactionMobile.Maui.BusinessLogic/Services/MerchantService.cs +++ b/TransactionMobile.Maui.BusinessLogic/Services/MerchantService.cs @@ -94,7 +94,7 @@ public async Task GetMerchantBalance(CancellationToken cancellationToke Guid estateId = this.ApplicationCache.GetEstateId(); Guid merchantId = this.ApplicationCache.GetMerchantId(); - Shared.Logger.Logger.LogInformation($"About to request merchant merchant balance"); + Shared.Logger.Logger.LogInformation($"About to request merchant balance"); Shared.Logger.Logger.LogDebug($"Merchant Balance Request details: Estate Id {estateId} Merchant Id {merchantId} Access Token {accessToken.AccessToken}"); MerchantBalanceResponse merchantBalance = await this.EstateClient.GetMerchantBalance(accessToken.AccessToken, estateId, merchantId, cancellationToken); @@ -104,4 +104,43 @@ public async Task GetMerchantBalance(CancellationToken cancellationToke return merchantBalance.AvailableBalance; } + + public async Task GetMerchantDetails(CancellationToken cancellationToken) { + TokenResponseModel accessToken = this.ApplicationCache.GetAccessToken(); + Guid estateId = this.ApplicationCache.GetEstateId(); + Guid merchantId = this.ApplicationCache.GetMerchantId(); + + Shared.Logger.Logger.LogInformation($"About to request merchant details"); + Shared.Logger.Logger.LogDebug($"Merchant Details Request details: Estate Id {estateId} Merchant Id {merchantId} Access Token {accessToken.AccessToken}"); + + MerchantResponse merchantResponse = await this.EstateClient.GetMerchant(accessToken.AccessToken, estateId, merchantId, cancellationToken); + + Shared.Logger.Logger.LogInformation($"Merchant details requested successfully"); + Shared.Logger.Logger.LogDebug($"Merchant Details Response: [{JsonConvert.SerializeObject(merchantResponse)}]"); + + MerchantDetailsModel model = new MerchantDetailsModel { + MerchantName = merchantResponse.MerchantName, + NextStatementDate = merchantResponse.NextStatementDate, + LastStatementDate = new DateTime(), + SettlementSchedule = merchantResponse.SettlementSchedule.ToString(), + AvailableBalance = merchantResponse.AvailableBalance, + Balance = merchantResponse.Balance, + Contact = new ContactModel { + Name = merchantResponse.Contacts.First().ContactName, + EmailAddress = merchantResponse.Contacts.First().ContactEmailAddress, + MobileNumber = merchantResponse.Contacts.First().ContactPhoneNumber + }, + Address = new AddressModel { + AddressLine3 = merchantResponse.Addresses.First().AddressLine3, + Town = merchantResponse.Addresses.First().Town, + AddressLine4 = merchantResponse.Addresses.First().AddressLine4, + PostalCode = merchantResponse.Addresses.First().PostalCode, + Region = merchantResponse.Addresses.First().Region, + AddressLine1 = merchantResponse.Addresses.First().AddressLine1, + AddressLine2 = merchantResponse.Addresses.First().AddressLine2 + } + }; + + return model; + } } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/UIServices/INavigationService.cs b/TransactionMobile.Maui.BusinessLogic/UIServices/INavigationService.cs index 10d152c8..237edece 100644 --- a/TransactionMobile.Maui.BusinessLogic/UIServices/INavigationService.cs +++ b/TransactionMobile.Maui.BusinessLogic/UIServices/INavigationService.cs @@ -40,5 +40,9 @@ Task GoToVoucherIssueVoucherPage(String operatorIdentifier, Task GoToViewLogsPage(); + Task GoToMyAccountAddresses(); + Task GoToMyAccountContacts(); + Task GoToMyAccountDetails(); + #endregion } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountAddressPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountAddressPageViewModel.cs new file mode 100644 index 00000000..94356840 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountAddressPageViewModel.cs @@ -0,0 +1,48 @@ +namespace TransactionMobile.Maui.BusinessLogic.ViewModels.MyAccount; + +using Maui.UIServices; +using MediatR; +using Models; +using MvvmHelpers; +using Services; + +public class MyAccountAddressPageViewModel : BaseViewModel +{ + private readonly INavigationService NavigationService; + + private readonly IApplicationCache ApplicationCache; + + private readonly IMediator Mediator; + + #region Constructors + + public MyAccountAddressPageViewModel(INavigationService navigationService, + IApplicationCache applicationCache, + IMediator mediator) { + this.NavigationService = navigationService; + this.ApplicationCache = applicationCache; + this.Mediator = mediator; + this.Title = "My Addresses"; + } + + #endregion + + private AddressModel address; + + public AddressModel Address{ + get => this.address; + set => this.SetProperty(ref this.address, value); + } + + #region Properties + + public async Task Initialise(CancellationToken cancellationToken) { + MerchantDetailsModel merchantDetails = this.ApplicationCache.GetMerchantDetails(); + + // TODO: handle a null + + this.Address = merchantDetails.Address; + } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountContactPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountContactPageViewModel.cs new file mode 100644 index 00000000..540c237e --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountContactPageViewModel.cs @@ -0,0 +1,48 @@ +namespace TransactionMobile.Maui.BusinessLogic.ViewModels.MyAccount; + +using Maui.UIServices; +using MediatR; +using MvvmHelpers; +using Services; +using TransactionMobile.Maui.BusinessLogic.Models; + +public class MyAccountContactPageViewModel : BaseViewModel +{ + private readonly INavigationService NavigationService; + + private readonly IApplicationCache ApplicationCache; + + private readonly IMediator Mediator; + + private ContactModel contact; + + public ContactModel Contact { + get => this.contact; + set => this.SetProperty(ref this.contact, value); + } + + #region Constructors + + public MyAccountContactPageViewModel(INavigationService navigationService, IApplicationCache applicationCache, + IMediator mediator) + { + this.NavigationService = navigationService; + this.ApplicationCache = applicationCache; + this.Mediator = mediator; + this.Title = "My Contacts"; + } + + + #endregion + + #region Properties + + #endregion + + public async Task Initialise(CancellationToken none) { + MerchantDetailsModel merchantDetails = this.ApplicationCache.GetMerchantDetails(); + + // TODO: handle a null + this.Contact = merchantDetails.Contact; + } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountDetailsPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountDetailsPageViewModel.cs new file mode 100644 index 00000000..939a89de --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountDetailsPageViewModel.cs @@ -0,0 +1,94 @@ +namespace TransactionMobile.Maui.BusinessLogic.ViewModels.MyAccount; + +using Maui.UIServices; +using MediatR; +using Models; +using MvvmHelpers; +using Services; + +public class MyAccountDetailsPageViewModel : BaseViewModel +{ + #region Fields + + private readonly IApplicationCache ApplicationCache; + + private Decimal availableBalance; + + private Decimal balance; + + private DateTime lastStatementDate; + + private readonly IMediator Mediator; + + private String merchantName; + + private readonly INavigationService NavigationService; + + private DateTime nextStatementDate; + + private String settlementSchedule; + + #endregion + + #region Constructors + + public MyAccountDetailsPageViewModel(INavigationService navigationService, + IApplicationCache applicationCache, + IMediator mediator) { + this.NavigationService = navigationService; + this.ApplicationCache = applicationCache; + this.Mediator = mediator; + this.Title = "My Details"; + } + + #endregion + + #region Properties + + public Decimal AvailableBalance { + get => this.availableBalance; + set => this.SetProperty(ref this.availableBalance, value); + } + + public Decimal Balance { + get => this.balance; + set => this.SetProperty(ref this.balance, value); + } + + public DateTime LastStatementDate { + get => this.lastStatementDate; + set => this.SetProperty(ref this.lastStatementDate, value); + } + + public String MerchantName { + get => this.merchantName; + set => this.SetProperty(ref this.merchantName, value); + } + + public DateTime NextStatementDate { + get => this.nextStatementDate; + set => this.SetProperty(ref this.nextStatementDate, value); + } + + public String SettlementSchedule { + get => this.settlementSchedule; + set => this.SetProperty(ref this.settlementSchedule, value); + } + + #endregion + + #region Methods + + public async Task Initialise(CancellationToken none) { + MerchantDetailsModel merchantDetails = this.ApplicationCache.GetMerchantDetails(); + + this.Balance = merchantDetails.Balance; + this.AvailableBalance = merchantDetails.AvailableBalance; + this.NextStatementDate = merchantDetails.NextStatementDate; + this.LastStatementDate = merchantDetails.LastStatementDate; + this.MerchantName = merchantDetails.MerchantName; + this.SettlementSchedule = merchantDetails.SettlementSchedule; + } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountPageViewModel.cs index 5ae481b0..6744869f 100644 --- a/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountPageViewModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/MyAccount/MyAccountPageViewModel.cs @@ -1,24 +1,43 @@ namespace TransactionMobile.Maui.BusinessLogic.ViewModels.MyAccount { using System.Windows.Input; + using Common; + using Maui.UIServices; + using MediatR; + using Microsoft.Extensions.Caching.Memory; + using Microsoft.Extensions.Primitives; + using Models; using MvvmHelpers; using MvvmHelpers.Commands; - using TransactionMobile.Maui.BusinessLogic.Services; - using TransactionMobile.Maui.UIServices; + using Requests; + using Services; + using Shared.Logger; public class MyAccountPageViewModel : BaseViewModel { - private readonly INavigationService NavigationService; + #region Fields private readonly IApplicationCache ApplicationCache; + private DateTime lastLogin; + + private readonly IMediator Mediator; + + private String merchantName; + + private readonly INavigationService NavigationService; + + #endregion + #region Constructors - public MyAccountPageViewModel(INavigationService navigationService,IApplicationCache applicationCache) - { + public MyAccountPageViewModel(INavigationService navigationService, + IApplicationCache applicationCache, + IMediator mediator) { this.NavigationService = navigationService; this.ApplicationCache = applicationCache; - this.LogoutCommand = new AsyncCommand(this.LogoutCommandExecute); + this.Mediator = mediator; + this.OptionSelectedCommand = new AsyncCommand>(this.OptionSelectedCommandExecute); this.Title = "My Account"; } @@ -26,20 +45,96 @@ public MyAccountPageViewModel(INavigationService navigationService,IApplicationC #region Properties - public ICommand LogoutCommand { get; set; } - + public DateTime LastLogin { + get => this.lastLogin; + set => this.SetProperty(ref this.lastLogin, value); + } + + public String MerchantName { + get => this.merchantName; + set => this.SetProperty(ref this.merchantName, value); + } + + public List MyAccountOptions { get; set; } + + public ICommand OptionSelectedCommand { get; set; } + #endregion #region Methods - private async Task LogoutCommandExecute() - { - Shared.Logger.Logger.LogInformation("LogoutCommand called"); + public async Task Initialise(CancellationToken cancellationToken) { + this.MyAccountOptions = new List { + new ListViewItem { + Title = "Addresses" + }, + new ListViewItem { + Title = "Contacts" + }, + new ListViewItem { + Title = "Account Info" + }, + new ListViewItem { + Title = "Logout" + } + }; + + GetMerchantDetailsRequest request = GetMerchantDetailsRequest.Create(); + + MerchantDetailsModel merchantDetailsModel = await this.Mediator.Send(request, cancellationToken); + this.MerchantName = merchantDetailsModel.MerchantName; + + DateTime expirationTime = DateTime.Now.AddMinutes(60); + CancellationChangeToken expirationToken = new CancellationChangeToken(new CancellationTokenSource(TimeSpan.FromMinutes(60)).Token); + MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions() + // Pin to cache. + .SetPriority(CacheItemPriority.NeverRemove) + // Set the actual expiration time + .SetAbsoluteExpiration(expirationTime) + // Force eviction to run + .AddExpirationToken(expirationToken); + + this.ApplicationCache.SetMerchantDetails(merchantDetailsModel, cacheEntryOptions); + + this.LastLogin = DateTime.Now; // TODO: might cache this in the application + } + + private async Task LogoutCommandExecute() { + Logger.LogInformation("LogoutCommand called"); this.ApplicationCache.SetAccessToken(null); - + await this.NavigationService.GoToLoginPage(); } - + + private async Task OptionSelectedCommandExecute(ItemSelected arg) { + AccountOptions selectedOption = (AccountOptions)arg.SelectedItemIndex; + + Task navigationTask = selectedOption switch { + AccountOptions.Addresses => this.NavigationService.GoToMyAccountAddresses(), + AccountOptions.Contacts => this.NavigationService.GoToMyAccountContacts(), + AccountOptions.AccountInfo => this.NavigationService.GoToMyAccountDetails(), + AccountOptions.Logout => this.LogoutCommandExecute(), + _ => Task.Factory.StartNew(() => Logger.LogWarning($"Unsupported option selected {selectedOption}")) + }; + + await navigationTask; + } + + #endregion + + #region Others + + public enum AccountOptions + { + Addresses = 0, + + Contacts = 1, + + AccountInfo = 2, + + Logout = 3 + } + #endregion } } \ No newline at end of file diff --git a/TransactionMobile.Maui.UiTests/Drivers/AppiumDriver.cs b/TransactionMobile.Maui.UiTests/Drivers/AppiumDriver.cs index 10b6ce92..4f55becb 100644 --- a/TransactionMobile.Maui.UiTests/Drivers/AppiumDriver.cs +++ b/TransactionMobile.Maui.UiTests/Drivers/AppiumDriver.cs @@ -46,6 +46,7 @@ public void StartApp() AppiumDriverWrapper.SetupiOSDriver(appiumService); } + //AppiumDriverWrapper.Driver.StartRecordingScreen(); } catch (Exception e) { @@ -105,6 +106,12 @@ private static void SetupAndroidDriver(AppiumLocalService appiumService) { public void StopApp() { AppiumDriverWrapper.Driver?.CloseApp(); + + //String video = AppiumDriverWrapper.Driver.StopRecordingScreen(); + //byte[] decode = Convert.FromBase64String(video); + + //String fileName = "c:\\temp\\VideoRecording_test.mp4"; + //File.WriteAllBytes(fileName, decode); ; } } } diff --git a/TransactionMobile.Maui.UiTests/Features/Login.feature b/TransactionMobile.Maui.UiTests/Features/Login.feature index 6f810cc4..5dbec73b 100644 --- a/TransactionMobile.Maui.UiTests/Features/Login.feature +++ b/TransactionMobile.Maui.UiTests/Features/Login.feature @@ -1,4 +1,4 @@ -@background @login +@background @login @toolbar @profile Feature: Login Background: diff --git a/TransactionMobile.Maui.UiTests/Features/Login.feature.cs b/TransactionMobile.Maui.UiTests/Features/Login.feature.cs index 33446da6..e13a0605 100644 --- a/TransactionMobile.Maui.UiTests/Features/Login.feature.cs +++ b/TransactionMobile.Maui.UiTests/Features/Login.feature.cs @@ -23,6 +23,8 @@ namespace TransactionMobile.Maui.UiTests.Features [NUnit.Framework.DescriptionAttribute("Login")] [NUnit.Framework.CategoryAttribute("background")] [NUnit.Framework.CategoryAttribute("login")] + [NUnit.Framework.CategoryAttribute("toolbar")] + [NUnit.Framework.CategoryAttribute("profile")] public partial class LoginFeature { @@ -30,7 +32,9 @@ public partial class LoginFeature private static string[] featureTags = new string[] { "background", - "login"}; + "login", + "toolbar", + "profile"}; #line 1 "Login.feature" #line hidden diff --git a/TransactionMobile.Maui.UiTests/Features/LoginFeature.cs b/TransactionMobile.Maui.UiTests/Features/LoginFeature.cs index 498cf8ae..b21cec8e 100644 --- a/TransactionMobile.Maui.UiTests/Features/LoginFeature.cs +++ b/TransactionMobile.Maui.UiTests/Features/LoginFeature.cs @@ -13,4 +13,14 @@ public LoginFeature(MobileTestPlatform mobileTestPlatform) : base(mobileTestPlatform) { } +} + +[TestFixture(MobileTestPlatform.Android, Category = "Android")] +[TestFixture(MobileTestPlatform.iOS, Category = "iOS")] +public partial class ProfileFeature : BaseTestFixture +{ + public ProfileFeature(MobileTestPlatform mobileTestPlatform) + : base(mobileTestPlatform) + { + } } \ No newline at end of file diff --git a/TransactionMobile.Maui.UiTests/Features/Profile.feature b/TransactionMobile.Maui.UiTests/Features/Profile.feature new file mode 100644 index 00000000..782a18b9 --- /dev/null +++ b/TransactionMobile.Maui.UiTests/Features/Profile.feature @@ -0,0 +1,34 @@ +@background @login @profile @toolbar +Feature: Profile + +Background: + Given I am on the Login Screen + And the application is in training mode + When I enter 'merchantuser@testmerchant1.co.uk' as the Email Address + And I enter '123456' as the Password + And I tap on Login + Then the Merchant Home Page is displayed + When I tap on Profile + Then the My Profile Page is displayed + +Scenario: View Merchant Addresses + When I tap on the Addresses button + Then the Address List Page is displayed + And the Primary Address is displayed + | AddressLine1 | AddressLine2 | AddressLine3 | AddressLine4 | AddressTown | AddressRegion | AddressPostCode | + | test address line 1 | test address line 2 | test address line 3 | test address line 4 | Town | Region | TE57 1NG | + +Scenario: View Merchant Contacts + When I tap on the Contacts button + Then the Contact List Page is displayed + And the Primary Contact is displayed + | Name | EmailAddress | MobileNumber | + | Test Contact | stuart_ferguson1@outlook.com | 123456789 | + +@PRTest +Scenario: View Merchant Details + When I tap on the Account Info button + Then the Account Info Page is displayed + And the Account Info is displayed + | Name | Balance | AvailableBalance | LastStatementDate | NextStatementDate | SettlementSchedule | + | Dummy Merchant | 99 | 100 | 01/08/2022 | 01/09/2022 | Monthly | diff --git a/TransactionMobile.Maui.UiTests/Features/Profile.feature.cs b/TransactionMobile.Maui.UiTests/Features/Profile.feature.cs new file mode 100644 index 00000000..49ac1d8b --- /dev/null +++ b/TransactionMobile.Maui.UiTests/Features/Profile.feature.cs @@ -0,0 +1,253 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by SpecFlow (https://www.specflow.org/). +// SpecFlow Version:3.9.0.0 +// SpecFlow Generator Version:3.9.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +#region Designer generated code +#pragma warning disable +namespace TransactionMobile.Maui.UiTests.Features +{ + using TechTalk.SpecFlow; + using System; + using System.Linq; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.9.0.0")] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [NUnit.Framework.TestFixtureAttribute()] + [NUnit.Framework.DescriptionAttribute("Profile")] + [NUnit.Framework.CategoryAttribute("background")] + [NUnit.Framework.CategoryAttribute("login")] + [NUnit.Framework.CategoryAttribute("profile")] + [NUnit.Framework.CategoryAttribute("toolbar")] + public partial class ProfileFeature + { + + private TechTalk.SpecFlow.ITestRunner testRunner; + + private static string[] featureTags = new string[] { + "background", + "login", + "profile", + "toolbar"}; + +#line 1 "Profile.feature" +#line hidden + + [NUnit.Framework.OneTimeSetUpAttribute()] + public virtual void FeatureSetup() + { + testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); + TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Profile", null, ProgrammingLanguage.CSharp, featureTags); + testRunner.OnFeatureStart(featureInfo); + } + + [NUnit.Framework.OneTimeTearDownAttribute()] + public virtual void FeatureTearDown() + { + testRunner.OnFeatureEnd(); + testRunner = null; + } + + [NUnit.Framework.SetUpAttribute()] + public void TestInitialize() + { + } + + [NUnit.Framework.TearDownAttribute()] + public void TestTearDown() + { + testRunner.OnScenarioEnd(); + } + + public void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) + { + testRunner.OnScenarioInitialize(scenarioInfo); + testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); + } + + public void ScenarioStart() + { + testRunner.OnScenarioStart(); + } + + public void ScenarioCleanup() + { + testRunner.CollectScenarioErrors(); + } + + public virtual void FeatureBackground() + { +#line 4 +#line hidden +#line 5 + testRunner.Given("I am on the Login Screen", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); +#line hidden +#line 6 + testRunner.And("the application is in training mode", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); +#line hidden +#line 7 + testRunner.When("I enter \'merchantuser@testmerchant1.co.uk\' as the Email Address", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 8 + testRunner.And("I enter \'123456\' as the Password", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); +#line hidden +#line 9 + testRunner.And("I tap on Login", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); +#line hidden +#line 10 + testRunner.Then("the Merchant Home Page is displayed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden +#line 11 + testRunner.When("I tap on Profile", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 12 + testRunner.Then("the My Profile Page is displayed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("View Merchant Addresses")] + public void ViewMerchantAddresses() + { + string[] tagsOfScenario = ((string[])(null)); + System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("View Merchant Addresses", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 14 +this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 4 +this.FeatureBackground(); +#line hidden +#line 15 + testRunner.When("I tap on the Addresses button", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 16 + testRunner.Then("the Address List Page is displayed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden + TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] { + "AddressLine1", + "AddressLine2", + "AddressLine3", + "AddressLine4", + "AddressTown", + "AddressRegion", + "AddressPostCode"}); + table1.AddRow(new string[] { + "test address line 1", + "test address line 2", + "test address line 3", + "test address line 4", + "Town", + "Region", + "TE57 1NG"}); +#line 17 + testRunner.And("the Primary Address is displayed", ((string)(null)), table1, "And "); +#line hidden + } + this.ScenarioCleanup(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("View Merchant Contacts")] + public void ViewMerchantContacts() + { + string[] tagsOfScenario = ((string[])(null)); + System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("View Merchant Contacts", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 21 +this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 4 +this.FeatureBackground(); +#line hidden +#line 22 + testRunner.When("I tap on the Contacts button", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 23 + testRunner.Then("the Contact List Page is displayed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden + TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { + "Name", + "EmailAddress", + "MobileNumber"}); + table2.AddRow(new string[] { + "Test Contact", + "stuart_ferguson1@outlook.com", + "123456789"}); +#line 24 + testRunner.And("the Primary Contact is displayed", ((string)(null)), table2, "And "); +#line hidden + } + this.ScenarioCleanup(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("View Merchant Details")] + public void ViewMerchantDetails() + { + string[] tagsOfScenario = ((string[])(null)); + System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("View Merchant Details", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 28 +this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + this.ScenarioStart(); +#line 4 +this.FeatureBackground(); +#line hidden +#line 29 + testRunner.When("I tap on the Account Info button", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); +#line hidden +#line 30 + testRunner.Then("the Account Info Page is displayed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line hidden + TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] { + "Name", + "Balance", + "AvailableBalance", + "LastStatementDate", + "NextStatementDate", + "SettlementSchedule"}); + table3.AddRow(new string[] { + "Dummy Merchant", + "99", + "100", + "01/08/2022", + "01/09/2022", + "Monthly"}); +#line 31 + testRunner.And("the Account Info is displayed", ((string)(null)), table3, "And "); +#line hidden + } + this.ScenarioCleanup(); + } + } +} +#pragma warning restore +#endregion diff --git a/TransactionMobile.Maui.UiTests/Pages/BasePage.cs b/TransactionMobile.Maui.UiTests/Pages/BasePage.cs index 61c12312..d5ca5887 100644 --- a/TransactionMobile.Maui.UiTests/Pages/BasePage.cs +++ b/TransactionMobile.Maui.UiTests/Pages/BasePage.cs @@ -22,7 +22,7 @@ public async Task AssertOnPage(TimeSpan? timeout = null) await Retry.For(async () => { - String message = $"Unable to verify on page: {this.GetType().Name} {Environment.NewLine} Source: {AppiumDriverWrapper.Driver.PageSource}"; + String message = $"Unable to verify on page: {this.GetType().Name} with trait {this.Trait} {Environment.NewLine} Source: {AppiumDriverWrapper.Driver.PageSource}"; Should.NotThrow(() => this.WaitForElementByAccessibilityId(this.Trait), message); }, @@ -36,8 +36,8 @@ await Retry.For(async () => /// Time to wait before the assertion fails public void WaitForPageToLeave(TimeSpan? timeout = null) { - timeout = timeout ?? TimeSpan.FromSeconds(5); - var message = "Unable to verify *not* on page: " + this.GetType().Name; + timeout = timeout ?? TimeSpan.FromSeconds(60); + String message = "Unable to verify *not* on page: " + this.GetType().Name; Should.NotThrow(() => this.WaitForNoElementByAccessibilityId(this.Trait), message); } @@ -87,5 +87,11 @@ public void NavigateBack() { AppiumDriverWrapper.Driver.Navigate().Back(); } + + public async Task GetLabelValue(String labelAutomationId) + { + IWebElement element = await this.WaitForElementByAccessibilityId(labelAutomationId); + return element.Text; + } } } diff --git a/TransactionMobile.Maui.UiTests/Pages/Extenstions.cs b/TransactionMobile.Maui.UiTests/Pages/Extenstions.cs index e1b31697..f1bd4e60 100644 --- a/TransactionMobile.Maui.UiTests/Pages/Extenstions.cs +++ b/TransactionMobile.Maui.UiTests/Pages/Extenstions.cs @@ -31,9 +31,11 @@ public static async Task WaitForElementByAccessibilityId(this Appiu timeout ??= TimeSpan.FromSeconds(60); await Retry.For(async () => { for (int i = 0; i < 10; i++) { + + String message = $"Unable to find element on page: {selector} {Environment.NewLine} Source: {AppiumDriverWrapper.Driver.PageSource}"; driver.ScrollDown(); element = driver.FindElement(MobileBy.AccessibilityId(selector)); - element.ShouldNotBeNull(); + element.ShouldNotBeNull(message); // All good so exit the loop break; } @@ -73,7 +75,7 @@ await Retry.For(async () => { IWebElement? element = driver.FindElement(MobileBy.AccessibilityId(selector)); element.ShouldBeNull(); - }); + }, timeout); } diff --git a/TransactionMobile.Maui.UiTests/Pages/ProfilePage.cs b/TransactionMobile.Maui.UiTests/Pages/ProfilePage.cs index 2cfd244d..ea2dc576 100644 --- a/TransactionMobile.Maui.UiTests/Pages/ProfilePage.cs +++ b/TransactionMobile.Maui.UiTests/Pages/ProfilePage.cs @@ -8,14 +8,26 @@ namespace TransactionMobile.Maui.UiTests.Pages { + using Drivers; + using OpenQA.Selenium.Appium.Android; + using Shouldly; + using UITests.Common; + public class ProfilePage : BasePage { protected override String Trait => "My Account"; private readonly String LogoutButton; + private readonly String AddressesButton; + private readonly String ContactsButton; + private readonly String AccountInfoButton; + public ProfilePage() { this.LogoutButton = "LogoutButton"; + this.AddressesButton = "AddressesButton"; + this.ContactsButton = "ContactsButton"; + this.AccountInfoButton = "AccountInfoButton"; } public async Task ClickLogoutButton() @@ -23,5 +35,171 @@ public async Task ClickLogoutButton() IWebElement element = await this.WaitForElementByAccessibilityId(this.LogoutButton); element.Click(); } + + public async Task ClickAddressesButton() + { + IWebElement element = await this.WaitForElementByAccessibilityId(this.AddressesButton); + element.Click(); + + var x = await AppiumDriverWrapper.Driver.GetPageSource(); + } + + public async Task ClickContactsButton() + { + IWebElement element = await this.WaitForElementByAccessibilityId(this.ContactsButton); + element.Click(); + } + + public async Task ClickAccountInfoButton() + { + IWebElement element = await this.WaitForElementByAccessibilityId(this.AccountInfoButton); + element.Click(); + } + } + + public class ProfileAddressesPage : BasePage + { + public readonly String PrimaryAddressLabel; + + private readonly String AddressLine1Label; + + private readonly String AddressLine2Label; + + private readonly String AddressLine3Label; + + private readonly String AddressLine4Label; + + private readonly String AddressRegionLabel; + + private readonly String AddressTownLabel; + + private readonly String AddressPostalCodeLabel; + + protected override String Trait => "My Addresses"; + + public ProfileAddressesPage() { + this.PrimaryAddressLabel = "PrimaryAddressLabel"; + this.AddressLine1Label = "AddressLine1Label"; + this.AddressLine2Label = "AddressLine2Label"; + this.AddressLine3Label = "AddressLine3Label"; + this.AddressLine4Label = "AddressLine4Label"; + this.AddressRegionLabel = "AddressRegionLabel"; + this.AddressTownLabel = "AddressTownLabel"; + this.AddressPostalCodeLabel = "AddressPostCodeLabel"; + } + + public async Task IsPrimaryAddressShown() { + await this.WaitForElementByAccessibilityId(this.PrimaryAddressLabel); + } + + public async Task GetAddressLineValue(Int32 lineNumber) { + return lineNumber switch { + 1 => await this.GetLabelValue(this.AddressLine1Label), + 2 => await this.GetLabelValue(this.AddressLine2Label), + 3 => await this.GetLabelValue(this.AddressLine3Label), + 4 => await this.GetLabelValue(this.AddressLine4Label) + }; + } + public async Task GetAddressRegionValue() + { + return await this.GetLabelValue(this.AddressRegionLabel); + } + public async Task GetAddressTownValue() + { + return await this.GetLabelValue(this.AddressTownLabel); + } + public async Task GetAddressPostalCodeValue() + { + return await this.GetLabelValue(this.AddressPostalCodeLabel); + } + } + + public class ProfileContactsPage : BasePage + { + public readonly String PrimaryContactLabel; + + private readonly String ContactNameLabel; + + private readonly String ContactEmailAddressLabel; + + private readonly String ContactMobileNumberLabel; + + protected override String Trait => "My Contacts"; + + public ProfileContactsPage() { + this.PrimaryContactLabel = "PrimaryContactLabel"; + this.ContactNameLabel = "ContactNameLabel"; + this.ContactEmailAddressLabel = "ContactEmailAddressLabel"; + this.ContactMobileNumberLabel = "ContactMobileNumberLabel"; + } + + public async Task IsPrimaryContactShown() + { + await this.WaitForElementByAccessibilityId(this.PrimaryContactLabel); + } + + public async Task GetContactNameValue() + { + return await this.GetLabelValue(this.ContactNameLabel); + } + public async Task GetContactEmailAddressValue() + { + return await this.GetLabelValue(this.ContactEmailAddressLabel); + } + public async Task GetContactMobileNumberValue() + { + return await this.GetLabelValue(this.ContactMobileNumberLabel); + } + } + + public class ProfileAccountInfoPage : BasePage + { + protected override String Trait => "My Details"; + + public readonly String MerchantNameLabel; + public readonly String BalanceLabel; + public readonly String AvailableBalanceLabel; + public readonly String LastStatementDateLabel; + public readonly String NextStatementDateLabel; + public readonly String SettlementScheduleLabel; + + public ProfileAccountInfoPage() { + this.MerchantNameLabel = "MerchantNameLabel"; + this.BalanceLabel = "BalanceLabel"; + this.AvailableBalanceLabel = "AvailableBalanceLabel"; + this.LastStatementDateLabel = "LastStatementDateLabel"; + this.NextStatementDateLabel = "NextStatementDateLabel"; + this.SettlementScheduleLabel = "SettlementScheduleLabel"; + } + + public async Task GetMerchantNameValue() + { + return await this.GetLabelValue(this.MerchantNameLabel); + } + + public async Task GetBalanceValue() + { + return await this.GetLabelValue(this.BalanceLabel); + } + + public async Task GetAvailableBalanceValue() + { + return await this.GetLabelValue(this.AvailableBalanceLabel); + } + + public async Task GetLastStatementDateValue() + { + return await this.GetLabelValue(this.LastStatementDateLabel); + } + + public async Task GetNextStatementDateValue() + { + return await this.GetLabelValue(this.NextStatementDateLabel); + } + + public async Task GetSettlementScheduleValue() + { + return await this.GetLabelValue(this.SettlementScheduleLabel); + } } } diff --git a/TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs b/TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs index 3aeeff07..88ed462c 100644 --- a/TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs +++ b/TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs @@ -7,81 +7,57 @@ namespace TransactionMobile.Maui.UITests.Steps { using TechTalk.SpecFlow; - using UiTests.Pages; [Binding] [Scope(Tag = "login")] public class LoginSteps { LoginPage loginPage = new LoginPage(); + MainPage mainPage = new MainPage(); - ProfilePage profilePage = new ProfilePage(); [Given(@"I am on the Login Screen")] - public async Task GivenIAmOnTheLoginScreen() - { + public async Task GivenIAmOnTheLoginScreen() { await this.loginPage.AssertOnPage(); } [Given(@"the application is in training mode")] public async Task GivenTheApplicationIsInTrainingMode() { var isTrainingModeOn = await this.loginPage.IsTrainingModeOn(); - + if (isTrainingModeOn == false) await this.loginPage.SetTrainingModeOn(); } - [When(@"I enter '(.*)' as the Email Address")] - public async Task WhenIEnterAsTheEmailAddress(String emailAddress) - { + public async Task WhenIEnterAsTheEmailAddress(String emailAddress) { await this.loginPage.EnterEmailAddress(emailAddress); } [When(@"I enter '(.*)' as the Password")] - public async Task WhenIEnterAsThePassword(String password) - { + public async Task WhenIEnterAsThePassword(String password) { await this.loginPage.EnterPassword(password); } [When(@"I tap on Login")] - public async Task WhenITapOnLogin() - { + public async Task WhenITapOnLogin() { await this.loginPage.ClickLoginButton(); } [Then(@"the Merchant Home Page is displayed")] - public async Task ThenTheMerchantHomePageIsDisplayed() - { + public async Task ThenTheMerchantHomePageIsDisplayed() { await this.mainPage.AssertOnPage(); } [Then(@"the available balance is shown as (.*)")] - public async Task ThenTheAvailableBalanceIsShownAs(Decimal expectedAvailableBalance) - { + public async Task ThenTheAvailableBalanceIsShownAs(Decimal expectedAvailableBalance) { //Decimal availableBalance = await this.mainPage.GetAvailableBalanceValue(TimeSpan.FromSeconds(120)).ConfigureAwait(false); //availableBalance.ShouldBe(expectedAvailableBalance); } - [When(@"I tap on Profile")] - public async Task WhenITapOnProfile() { - await this.mainPage.ClickProfileButton(); - } - - [Then(@"the My Profile Page is displayed")] - public async Task ThenTheMyProfilePageIsDisplayed() { - await this.profilePage.AssertOnPage(); - } - - [When(@"I tap on Logout")] - public async Task WhenITapOnLogout() { - await this.profilePage.ClickLogoutButton(); - } - [Then(@"the Login Screen is displayed")] public async Task ThenTheLoginScreenIsDisplayed() { await this.loginPage.AssertOnPage(); } - } } diff --git a/TransactionMobile.Maui.UiTests/Steps/ProfileSteps.cs b/TransactionMobile.Maui.UiTests/Steps/ProfileSteps.cs new file mode 100644 index 00000000..8d3a9ae8 --- /dev/null +++ b/TransactionMobile.Maui.UiTests/Steps/ProfileSteps.cs @@ -0,0 +1,140 @@ +namespace TransactionMobile.Maui.UITests.Steps; + +using System; +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using TechTalk.SpecFlow; +using TransactionMobile.Maui.UiTests.Drivers; +using UiTests.Pages; + +[Binding] +[Scope(Tag = "profile")] +public class ProfileSteps +{ + ProfilePage profilePage = new ProfilePage(); + + ProfileAddressesPage profileAddressesPage = new ProfileAddressesPage(); + + ProfileContactsPage profileContactsPage = new ProfileContactsPage(); + ProfileAccountInfoPage profileAccountInfoPage = new ProfileAccountInfoPage(); + + [Then(@"the My Profile Page is displayed")] + public async Task ThenTheMyProfilePageIsDisplayed() + { + await this.profilePage.AssertOnPage(); + } + + [When(@"I tap on Logout")] + public async Task WhenITapOnLogout() + { + await this.profilePage.ClickLogoutButton(); + } + + [When(@"I tap on the Addresses button")] + public async Task WhenITapOnTheAddressesButton() { + await this.profilePage.ClickAddressesButton(); + } + + [Then(@"the Address List Page is displayed")] + public async Task ThenTheAddressListPageIsDisplayed() + { + await this.profileAddressesPage.AssertOnPage(); + } + + [Then(@"the Primary Address is displayed")] + public async Task ThenThePrimaryAddressIsDisplayed(Table table) + { + await this.profileAddressesPage.IsPrimaryAddressShown(); + + String addressLine1 = await this.profileAddressesPage.GetAddressLineValue(1); + String addressLine2 = await this.profileAddressesPage.GetAddressLineValue(2); + String addressLine3 = await this.profileAddressesPage.GetAddressLineValue(3); + String addressLine4 = await this.profileAddressesPage.GetAddressLineValue(4); + String addressTown = await this.profileAddressesPage.GetAddressTownValue(); + String addressRegion = await this.profileAddressesPage.GetAddressRegionValue(); + String addressPostalCode = await this.profileAddressesPage.GetAddressPostalCodeValue(); + + String? expectedAddressLine1 = table.Rows.Single()["AddressLine1"]; + String? expectedAddressLine2 = table.Rows.Single()["AddressLine2"]; + String? expectedAddressLine3 = table.Rows.Single()["AddressLine3"]; + String? expectedAddressLine4 = table.Rows.Single()["AddressLine4"]; + String? expectedAddressTown = table.Rows.Single()["AddressTown"]; + String? expectedAddressRegion = table.Rows.Single()["AddressRegion"]; + String? expectedAddressPostCode = table.Rows.Single()["AddressPostCode"]; + + addressLine1.ShouldBe(expectedAddressLine1); + addressLine2.ShouldBe(expectedAddressLine2); + addressLine3.ShouldBe(expectedAddressLine3); + addressLine4.ShouldBe(expectedAddressLine4); + addressTown.ShouldBe(expectedAddressTown); + addressRegion.ShouldBe(expectedAddressRegion); + addressPostalCode.ShouldBe(expectedAddressPostCode); + } + + [When(@"I tap on the Contacts button")] + public async Task WhenITapOnTheContactsButton() { + await this.profilePage.ClickContactsButton(); + } + + [Then(@"the Contact List Page is displayed")] + public async Task ThenTheContactListPageIsDisplayed() + { + await this.profileContactsPage.AssertOnPage(); + } + + [Then(@"the Primary Contact is displayed")] + public async Task ThenThePrimaryContactIsDisplayed(Table table) + { + await this.profileContactsPage.IsPrimaryContactShown(); + + String contactName = await this.profileContactsPage.GetContactNameValue(); + String contactEmailAddress = await this.profileContactsPage.GetContactEmailAddressValue(); + String contactMobileNumber = await this.profileContactsPage.GetContactMobileNumberValue(); + + String? expectedContactName = table.Rows.Single()["Name"]; + String? expectedContactEmailAddress = table.Rows.Single()["EmailAddress"]; + String? expectedContactMobileNumber = table.Rows.Single()["MobileNumber"]; + + contactName.ShouldBe(expectedContactName); + contactEmailAddress.ShouldBe(expectedContactEmailAddress); + contactMobileNumber.ShouldBe(expectedContactMobileNumber); + } + + [When(@"I tap on the Account Info button")] + public async Task WhenITapOnTheAccountInfoButton() { + await this.profilePage.ClickAccountInfoButton(); + } + + [Then(@"the Account Info Page is displayed")] + public async Task ThenTheAccountInfoPageIsDisplayed() + { + await this.profileAccountInfoPage.AssertOnPage(); + } + + [Then(@"the Account Info is displayed")] + public async Task ThenTheAccountInfoIsDisplayed(Table table) + { + String merchantName = await this.profileAccountInfoPage.GetMerchantNameValue(); + String balance = await this.profileAccountInfoPage.GetBalanceValue(); + String availableBalance = await this.profileAccountInfoPage.GetAvailableBalanceValue(); + String lastStatementDate = await this.profileAccountInfoPage.GetLastStatementDateValue(); + String nextStatementDate = await this.profileAccountInfoPage.GetNextStatementDateValue(); + String settlementSchedule = await this.profileAccountInfoPage.GetSettlementScheduleValue(); + + String? expectedMerchantName = table.Rows.Single()["Name"]; + String? expectedBalance = table.Rows.Single()["Balance"]; + String? expectedAvailableBalance = table.Rows.Single()["AvailableBalance"]; + String? expectedLastStatementDate = table.Rows.Single()["LastStatementDate"]; + String? expectedNextStatementDate = table.Rows.Single()["NextStatementDate"]; + String? expectedSettlementSchedule = table.Rows.Single()["SettlementSchedule"]; + + merchantName.ShouldBe(expectedMerchantName); + balance.ShouldBe(expectedBalance); + availableBalance.ShouldBe(expectedAvailableBalance); + lastStatementDate.ShouldBe(expectedLastStatementDate); + nextStatementDate.ShouldBe(expectedNextStatementDate); + settlementSchedule.ShouldBe(expectedSettlementSchedule); + } + +} \ No newline at end of file diff --git a/TransactionMobile.Maui.UiTests/Steps/ToolbarSteps.cs b/TransactionMobile.Maui.UiTests/Steps/ToolbarSteps.cs new file mode 100644 index 00000000..c2c28887 --- /dev/null +++ b/TransactionMobile.Maui.UiTests/Steps/ToolbarSteps.cs @@ -0,0 +1,17 @@ +namespace TransactionMobile.Maui.UITests.Steps; + +using System.Threading.Tasks; +using TechTalk.SpecFlow; + +[Binding] +[Scope(Tag = "toolbar")] +public class ToolbarSteps +{ + MainPage mainPage = new MainPage(); + + [When(@"I tap on Profile")] + public async Task WhenITapOnProfile() + { + await this.mainPage.ClickProfileButton(); + } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.UiTests/TransactionMobile.Maui.UiTests.csproj b/TransactionMobile.Maui.UiTests/TransactionMobile.Maui.UiTests.csproj index 02cee581..8809f31d 100644 --- a/TransactionMobile.Maui.UiTests/TransactionMobile.Maui.UiTests.csproj +++ b/TransactionMobile.Maui.UiTests/TransactionMobile.Maui.UiTests.csproj @@ -21,7 +21,6 @@ - diff --git a/TransactionMobile.Maui/App.xaml.cs b/TransactionMobile.Maui/App.xaml.cs index 22093bf7..a1315e08 100644 --- a/TransactionMobile.Maui/App.xaml.cs +++ b/TransactionMobile.Maui/App.xaml.cs @@ -9,6 +9,7 @@ namespace TransactionMobile.Maui; using Microsoft.Maui.Handlers; using Pages; using Pages.AppHome; +using Pages.MyAccount; using Pages.Support; using Pages.Transactions.Admin; using TransactionMobile.Maui.BusinessLogic.Services; @@ -139,6 +140,7 @@ public App() MainPage = new LoginPage(loginPageViewModel); } + // TODO: Investigate if this could be done automatically (maybe with exclusions for top level pages) Routing.RegisterRoute(nameof(MobileTopupSelectOperatorPage), typeof(MobileTopupSelectOperatorPage)); Routing.RegisterRoute(nameof(MobileTopupSelectProductPage), typeof(MobileTopupSelectProductPage)); Routing.RegisterRoute(nameof(MobileTopupPerformTopupPage), typeof(MobileTopupPerformTopupPage)); @@ -155,6 +157,10 @@ public App() Routing.RegisterRoute(nameof(LoginPage), typeof(LoginPage)); Routing.RegisterRoute(nameof(ViewLogsPage), typeof(ViewLogsPage)); + + Routing.RegisterRoute(nameof(MyAccountAddressesPage), typeof(MyAccountAddressesPage)); + Routing.RegisterRoute(nameof(MyAccountContactPage), typeof(MyAccountContactPage)); + Routing.RegisterRoute(nameof(MyAccountDetailsPage), typeof(MyAccountDetailsPage)); } } diff --git a/TransactionMobile.Maui/AppShell.xaml b/TransactionMobile.Maui/AppShell.xaml index ed1df41c..4fbbef64 100644 --- a/TransactionMobile.Maui/AppShell.xaml +++ b/TransactionMobile.Maui/AppShell.xaml @@ -17,7 +17,7 @@ + Route="home" Icon="homebutton"/> @@ -29,7 +29,7 @@ Route="myaccount" Icon="profilebutton"/> + Route="support" Icon="supportbutton" /> diff --git a/TransactionMobile.Maui/AppShell.xaml.cs b/TransactionMobile.Maui/AppShell.xaml.cs index c3345b52..e2078753 100644 --- a/TransactionMobile.Maui/AppShell.xaml.cs +++ b/TransactionMobile.Maui/AppShell.xaml.cs @@ -1,6 +1,10 @@ namespace TransactionMobile.Maui; +//using Android.Media; using System.Diagnostics; +using System.Windows.Input; +using Newtonsoft.Json; +using Shell = Microsoft.Maui.Controls.Shell; public partial class AppShell : Shell { @@ -10,10 +14,21 @@ public AppShell() } protected override void OnNavigating(ShellNavigatingEventArgs args) { + Shared.Logger.Logger.LogDebug($"In OnNavigating - Source [{args.Source.ToString()}] {JsonConvert.SerializeObject(args)}"); + if (args.Source == ShellNavigationSource.ShellSectionChanged) { + var existingPages = Navigation.NavigationStack.ToList(); + foreach (var page in existingPages) + { + if (page != null) { + Navigation.RemovePage(page); + } + } + } base.OnNavigating(args); } protected override void OnNavigated(ShellNavigatedEventArgs args) { + Shared.Logger.Logger.LogDebug($"In OnNavigated - Source [{args.Source.ToString()}] {JsonConvert.SerializeObject(args)}"); base.OnNavigated(args); } -} \ No newline at end of file +} diff --git a/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs b/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs index e78d40e1..1ec7f0a0 100644 --- a/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs +++ b/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs @@ -203,7 +203,8 @@ public static MauiAppBuilder ConfigureRequestHandlers(this MauiAppBuilder builde builder.Services.AddSingleton>, MerchantRequestHandler>(); builder.Services.AddSingleton, MerchantRequestHandler>(); - + builder.Services.AddSingleton, MerchantRequestHandler>(); + builder.Services.AddSingleton, TransactionRequestHandler>(); builder.Services.AddSingleton, TransactionRequestHandler>(); builder.Services.AddSingleton, TransactionRequestHandler>(); @@ -240,6 +241,9 @@ public static MauiAppBuilder ConfigureViewModels(this MauiAppBuilder builder) builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); builder.Services.AddTransient(); @@ -269,6 +273,9 @@ public static MauiAppBuilder ConfigurePages(this MauiAppBuilder builder) builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); builder.Services.AddTransient(); diff --git a/TransactionMobile.Maui/Pages/MyAccount/MyAccountAddressesPage.xaml b/TransactionMobile.Maui/Pages/MyAccount/MyAccountAddressesPage.xaml new file mode 100644 index 00000000..de72ebb1 --- /dev/null +++ b/TransactionMobile.Maui/Pages/MyAccount/MyAccountAddressesPage.xaml @@ -0,0 +1,43 @@ + + + + + + + + + +