Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using MediatR;
using SimpleResults;
using Shouldly;
using TransactionProcessor.Mobile.BusinessLogic.RequestHandlers;
using TransactionProcessor.Mobile.BusinessLogic.Requests;
using TransactionProcessor.Mobile.BusinessLogic.Models;

namespace TransactionProcessor.Mobile.BusinessLogic.Tests.RequestHandlerTests;

public class ReportRequestHandlerTests
{
[Fact]
public async Task GetDailyPerformanceSummaryQuery_ReturnsMockedSummaryForToday()
{
ReportRequestHandler handler = new();

Result<DailyPerformanceSummaryModel> result = await handler.Handle(new ReportQueries.GetDailyPerformanceSummaryQuery(PerformanceSummaryPeriod.Today), CancellationToken.None);

result.IsSuccess.ShouldBeTrue();
result.Data.Period.ShouldBe(PerformanceSummaryPeriod.Today);
result.Data.Metrics.ShouldContain(m => m.Title == "Total transaction count" && m.Value == "48");
result.Data.DrillDownTransactions.ShouldContain(t => t.Reference == "TXN-00048");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using MediatR;
using Moq;
using SimpleResults;
using Shouldly;
using TransactionProcessor.Mobile.BusinessLogic.Common;
using TransactionProcessor.Mobile.BusinessLogic.Models;
using TransactionProcessor.Mobile.BusinessLogic.Requests;
using TransactionProcessor.Mobile.BusinessLogic.Services;
using TransactionProcessor.Mobile.BusinessLogic.UIServices;
using TransactionProcessor.Mobile.BusinessLogic.ViewModels.Reports;

namespace TransactionProcessor.Mobile.BusinessLogic.Tests.ViewModelTests.Reports;

public class DailyPerformanceSummaryPageViewModelTests
{
private readonly Mock<IMediator> Mediator;
private readonly Mock<INavigationService> NavigationService;
private readonly Mock<IApplicationCache> ApplicationCache;
private readonly Mock<IDialogService> DialogService;
private readonly Mock<IDeviceService> DeviceService;
private readonly Mock<INavigationParameterService> NavigationParameterService;
private readonly DailyPerformanceSummaryPageViewModel ViewModel;

public DailyPerformanceSummaryPageViewModelTests()
{
this.Mediator = new Mock<IMediator>();
this.Mediator
.Setup(m => m.Send(It.IsAny<ReportQueries.GetDailyPerformanceSummaryQuery>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(Result.Success(DailyPerformanceSummaryModel.CreateMock(PerformanceSummaryPeriod.Today)));
this.NavigationService = new Mock<INavigationService>();
this.ApplicationCache = new Mock<IApplicationCache>();
this.DialogService = new Mock<IDialogService>();
this.DeviceService = new Mock<IDeviceService>();
this.NavigationParameterService = new Mock<INavigationParameterService>();

this.ViewModel = new DailyPerformanceSummaryPageViewModel(this.Mediator.Object,
this.NavigationService.Object,
this.ApplicationCache.Object,
this.DialogService.Object,
this.DeviceService.Object,
this.NavigationParameterService.Object);
}

[Fact]
public async Task Initialise_LoadsMockedSummaryForToday()
{
await this.ViewModel.Initialise(CancellationToken.None);

this.ViewModel.SelectedPeriod.ShouldBe(PerformanceSummaryPeriod.Today);
this.ViewModel.SummaryCards.Count.ShouldBe(6);
this.ViewModel.TopSummaryCards.Count.ShouldBe(4);
this.ViewModel.TopSummaryCardsRow1.Count.ShouldBe(2);
this.ViewModel.TopSummaryCardsRow2.Count.ShouldBe(2);
this.ViewModel.TopSummaryCardsRow1.Select(card => card.Title).ShouldBe(new[]
{
"Total transaction count",
"Total transaction value",
});
this.ViewModel.TopSummaryCardsRow2.Select(card => card.Title).ShouldBe(new[]
{
"Successful transaction count",
"Failed transaction count",
});
this.ViewModel.DrillDownTransactions.Count.ShouldBe(3);
this.ViewModel.IsLoading.ShouldBeFalse();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,10 @@ public async Task ReportsPageViewModel_OptionSelectedCommand_Execute_IsExecuted(

switch(selectedIndex){
case 0:
this.NavigationService.Verify(v => v.GoToReportsSalesAnalysis(), Times.Once);
break;
case 1:
this.NavigationService.Verify(v => v.GoToReportsBalanceAnalysis(), Times.Once);
this.NavigationService.Verify(v => v.GoToDailyPerformanceSummaryPage(), Times.Once);
break;
default:
this.NavigationService.Verify(v => v.GoToReportsSalesAnalysis(), Times.Never);
this.NavigationService.Verify(v => v.GoToReportsBalanceAnalysis(), Times.Never);
this.NavigationService.Verify(v => v.GoToDailyPerformanceSummaryPage(), Times.Never);
break;
}
}
Expand All @@ -70,7 +66,7 @@ public async Task ReportsPageViewModel_OptionSelectedCommand_Execute_IsExecuted(
public async Task ReportsPageViewModel_Initialise_IsInitialised()
{
await this.ViewModel.Initialise(CancellationToken.None);
this.ViewModel.ReportsMenuOptions.Count.ShouldBe(2);
this.ViewModel.ReportsMenuOptions.Count.ShouldBe(1);
}

[Fact]
Expand All @@ -81,117 +77,4 @@ public async Task ReportsPageViewModel_BackButtonCommand_HomePageIsShown()
this.NavigationService.Verify(n => n.GoToHome(), Times.Once);
}
}

public class ReportsSalesAnalysisPageViewModelTests{
private readonly ReportsSalesAnalysisPageViewModel ViewModel;
private readonly Mock<INavigationService> NavigationService;
private readonly Mock<INavigationParameterService> NavigationParameterService;
private readonly Mock<IApplicationCache> ApplicationCache;
private readonly Mock<IDialogService> DialogService;
private readonly Mock<IDeviceService> DeviceService;

public ReportsSalesAnalysisPageViewModelTests(){
this.NavigationService = new Mock<INavigationService>();
this.NavigationParameterService = new Mock<INavigationParameterService>();
this.ApplicationCache = new Mock<IApplicationCache>();
this.DialogService = new Mock<IDialogService>();
this.DeviceService = new Mock<IDeviceService>();

this.ViewModel = new ReportsSalesAnalysisPageViewModel(this.NavigationService.Object,
this.ApplicationCache.Object,
this.DialogService.Object,
this.DeviceService.Object,
this.NavigationParameterService.Object);
}

[Fact]
public async Task ReportsSalesAnalysisPageViewModel_Initialise_Execute_IsExecuted(){
await this.ViewModel.Initialise(CancellationToken.None);

this.ViewModel.ComparisonDates.Count.ShouldBe(3);
}

[Fact]
public async Task ReportsSalesAnalysisPageViewModel_ComparisonDatePickerSelectedIndexChangedCommand_HomePageIsShown(){
ComparisonDate comparisonDate = new ComparisonDate(DateTime.Now.AddDays(-1), "Yesterday");
this.ViewModel.SelectedItem = comparisonDate;
this.ViewModel.ComparisonDatePickerSelectedIndexChangedCommand.Execute(comparisonDate);

// Nothing to actually verify yet here
this.ViewModel.SalesAnalysisList.Count.ShouldBe(2);
}

[Fact]
public void ReportsSalesAnalysisPageViewModel_ComparisonDatePickerSelectedIndexChangedCommand_WithoutSelectedItem_ReturnsEmptyList()
{
this.ViewModel.ComparisonDatePickerSelectedIndexChangedCommand.Execute(null);

this.ViewModel.SalesAnalysisList.ShouldNotBeNull();
this.ViewModel.SalesAnalysisList.ShouldBeEmpty();
}

[Fact]
public async Task ReportsSalesAnalysisPageViewModel_BackButtonCommand_HomePageIsShown()
{
this.ViewModel.BackButtonCommand.Execute(null);

this.NavigationService.Verify(n => n.GoBack(), Times.Once);
}
}

public class ReportsBalanceAnalysisPageViewModelTests
{
private ReportsBalanceAnalysisPageViewModel ViewModel;
private Mock<INavigationService> NavigationService;
private Mock<INavigationParameterService> NavigationParameterService;
private Mock<IApplicationCache> ApplicationCache;
private Mock<IDialogService> DialogService;
private readonly Mock<IDeviceService> DeviceService;

public ReportsBalanceAnalysisPageViewModelTests()
{
this.NavigationService = new Mock<INavigationService>();
this.NavigationParameterService = new Mock<INavigationParameterService>();
this.ApplicationCache = new Mock<IApplicationCache>();
this.DialogService = new Mock<IDialogService>();
this.DeviceService = new Mock<IDeviceService>();

this.ViewModel = new ReportsBalanceAnalysisPageViewModel(this.NavigationService.Object,
this.ApplicationCache.Object,
this.DialogService.Object,
this.DeviceService.Object,
this.NavigationParameterService.Object);
}

[Fact]
public async Task ReportsBalanceAnalysisPageViewModel_Initialise_Execute_IsExecuted()
{
await this.ViewModel.Initialise(CancellationToken.None);

this.ViewModel.XAxes.ShouldNotBeNull();
this.ViewModel.YAxes.ShouldNotBeNull();
this.ViewModel.TooltipFindingStrategy.ShouldBe(TooltipFindingStrategy.CompareOnlyX);
this.ViewModel.TooltipPosition.ShouldBe(TooltipPosition.Top);
this.ViewModel.Series.ShouldNotBeNull();
}

//[Fact]
//public async Task ReportsSalesAnalysisPageViewModel_ComparisonDatePickerSelectedIndexChangedCommand_HomePageIsShown()
//{
// ComparisonDate comparisonDate = new ComparisonDate(DateTime.Now.AddDays(-1), "Yesterday");
// this.viewModel.SelectedItem = comparisonDate;
// viewModel.ComparisonDatePickerSelectedIndexChangedCommand.Execute(comparisonDate);

// // Nothing to actually verify yet here
// this.viewModel.SalesAnalysisList.Count.ShouldBe(2);
//}

[Fact]
public async Task ReportsBalanceAnalysisPageViewModel_BackButtonCommand_HomePageIsShown()
{
this.ViewModel.BackButtonCommand.Execute(null);

this.NavigationService.Verify(n => n.GoBack(), Times.Once);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
namespace TransactionProcessor.Mobile.BusinessLogic.Models;

public enum PerformanceSummaryPeriod
{
Today = 0,
Yesterday = 1,
ThisWeek = 2,
MonthToDate = 3,
}

public sealed record DailyPerformanceSummaryModel(
PerformanceSummaryPeriod Period,
DateTime FromDate,
DateTime ToDate,
string PeriodLabel,
IReadOnlyList<DailyPerformanceMetricModel> Metrics,
IReadOnlyList<DailyPerformanceTransactionModel> DrillDownTransactions)
{
public static DailyPerformanceSummaryModel CreateMock(PerformanceSummaryPeriod period)
{
DateTime today = DateTime.Today;
DateTime fromDate = period switch
{
PerformanceSummaryPeriod.Today => today,
PerformanceSummaryPeriod.Yesterday => today.AddDays(-1),
PerformanceSummaryPeriod.ThisWeek => StartOfWeek(today),
PerformanceSummaryPeriod.MonthToDate => new DateTime(today.Year, today.Month, 1),
_ => today,
};

DateTime toDate = period switch
{
PerformanceSummaryPeriod.Yesterday => today.AddDays(-1),
_ => today,
};

List<DailyPerformanceMetricModel> metrics = new()
{
new DailyPerformanceMetricModel("Total transaction count", "48", "Processed today", DailyPerformanceMetricCategory.Total),
new DailyPerformanceMetricModel("Total transaction value", "10,250.00 KES", "Gross value", DailyPerformanceMetricCategory.Total),
new DailyPerformanceMetricModel("Successful transaction count", "44", "Completed successfully", DailyPerformanceMetricCategory.Success),
new DailyPerformanceMetricModel("Failed transaction count", "4", "Could not be completed", DailyPerformanceMetricCategory.Failure),
new DailyPerformanceMetricModel("Average transaction value", "213.54 KES", "Average across all transactions"),
new DailyPerformanceMetricModel("Top product", "Mobile Topup", "Highest volume"),
};

List<DailyPerformanceTransactionModel> drillDownTransactions = new()
{
new DailyPerformanceTransactionModel("TXN-00048", "Mobile Topup", "Success", "250.00 KES", today.AddHours(-1)),
new DailyPerformanceTransactionModel("TXN-00047", "Bill Payment", "Success", "1,500.00 KES", today.AddHours(-2)),
new DailyPerformanceTransactionModel("TXN-00046", "Voucher Issue", "Failed", "0.00 KES", today.AddHours(-3)),
};

return new DailyPerformanceSummaryModel(period, fromDate, toDate, period.ToString(), metrics, drillDownTransactions);
}

private static DateTime StartOfWeek(DateTime date)
{
int daysSinceSunday = (int)date.DayOfWeek;
return date.Date.AddDays(-daysSinceSunday);
}
}

public enum DailyPerformanceMetricCategory
{
Neutral = 0,
Total = 1,
Success = 2,
Failure = 3,
}

public sealed record DailyPerformanceMetricModel(string Title, string Value, string Description, DailyPerformanceMetricCategory Category = DailyPerformanceMetricCategory.Neutral);

public sealed record DailyPerformanceTransactionModel(string Reference, string Product, string Status, string Amount, DateTime TransactionDateTime);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using MediatR;
using SimpleResults;
using TransactionProcessor.Mobile.BusinessLogic.Models;
using TransactionProcessor.Mobile.BusinessLogic.Requests;

namespace TransactionProcessor.Mobile.BusinessLogic.RequestHandlers;

public sealed class ReportRequestHandler : IRequestHandler<ReportQueries.GetDailyPerformanceSummaryQuery, Result<DailyPerformanceSummaryModel>>
{
public Task<Result<DailyPerformanceSummaryModel>> Handle(ReportQueries.GetDailyPerformanceSummaryQuery request, CancellationToken cancellationToken)
{
DailyPerformanceSummaryModel summary = DailyPerformanceSummaryModel.CreateMock(request.Period);
return Task.FromResult(Result.Success(summary));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using MediatR;
using SimpleResults;
using TransactionProcessor.Mobile.BusinessLogic.Models;

namespace TransactionProcessor.Mobile.BusinessLogic.Requests;

public record ReportQueries
{
public record GetDailyPerformanceSummaryQuery(PerformanceSummaryPeriod Period) : IRequest<Result<DailyPerformanceSummaryModel>>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,9 @@ Task GoToBillPaymentPayBillPage(ProductDetails productDetails,
Task GoToMyAccountContacts();
Task GoToMyAccountDetails();

Task GoToReportsSalesAnalysis();
Task GoToReportsBalanceAnalysis();
Task GoToDailyPerformanceSummaryPage();

Task GoToTransactions();

#endregion
}
}
Loading
Loading