Skip to content

Commit

Permalink
Merge pull request #164 from SkillsFundingAgency/EC-804
Browse files Browse the repository at this point in the history
EC-804
  • Loading branch information
DavidRimarDfE authored Jan 28, 2025
2 parents bcc140f + 66929fc commit 85df8f4
Show file tree
Hide file tree
Showing 10 changed files with 384 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using AutoFixture.NUnit3;

using Microsoft.AspNetCore.Mvc;
using Moq;
using SFA.DAS.ApprenticeAan.Domain.Interfaces;
using SFA.DAS.ApprenticeAan.Web.Infrastructure;
using SFA.DAS.ApprenticeAan.Web.Models.EventNotificationSettings;
using SFA.DAS.ApprenticeAan.Web.Models.Shared;
using SFA.DAS.Testing.AutoFixture;
using SFA.DAS.ApprenticeAan.Web.UnitTests.TestHelpers;
using FluentAssertions;
using FluentValidation;
using SFA.DAS.ApprenticeAan.Web.Models;
using FluentValidation.Results;
using SFA.DAS.ApprenticeAan.Web.Orchestrators;
using SFA.DAS.ApprenticeAan.Web.Controllers.EventNotificationSettings;

namespace SFA.DAS.ApprenticeAan.Web.UnitTests.Controllers.EventNotificationSettings;

[TestFixture]
public class ReceiveNotificationsControllerGetTests
{
[Test, MoqAutoData]
public void Get_WhenCalled_ReturnsViewResultWithViewModel(
[Frozen] Mock<ISessionService> mockSessionService,
string settingsUri,
NotificationSettingsSessionModel sessionModel,
CancellationToken cancellationToken,
[Greedy] ReceiveNotificationsController controller)
{
//Arrange
mockSessionService.Setup(s => s.Get<NotificationSettingsSessionModel>()).Returns(sessionModel);

controller.AddUrlHelperMock().AddUrlForRoute(RouteNames.EventNotificationSettings.Settings, settingsUri);

// Act
var result = controller.Get(cancellationToken) as ViewResult;

// Assert
result.Should().NotBeNull();
var viewModel = result.Model as ReceiveNotificationsViewModel;
viewModel.Should().NotBeNull();
viewModel.ReceiveNotifications.Should().Be(sessionModel.ReceiveNotifications);
}

[Test, MoqAutoData]
public async Task Post_UpdatesSessionModelAsync(
[Frozen] Mock<IValidator<ReceiveNotificationsSubmitModel>> mockValidator,
[Frozen] Mock<ISessionService> mockSessionService,
ReceiveNotificationsSubmitModel submitModel,
ValidationResult validationResult,
NotificationSettingsSessionModel sessionModel,
[Greedy] ReceiveNotificationsController controller)
{
// Arrange
sessionModel.ReceiveNotifications = null;
submitModel.ReceiveNotifications = true;
validationResult.Errors.Clear();
mockValidator.Setup(v => v.Validate(submitModel)).Returns(validationResult);
mockSessionService.Setup(s => s.Get<NotificationSettingsSessionModel>()).Returns(sessionModel);

// Act
var result = await controller.Post(submitModel, CancellationToken.None) as RedirectToRouteResult;

// Assert
mockSessionService.Verify(x => x.Set(It.Is<NotificationSettingsSessionModel>(s => s.ReceiveNotifications == submitModel.ReceiveNotifications)), Times.Once);
}

[TestCase(false, null, RouteNames.EventNotificationSettings.Settings)]
[TestCase(false, false, RouteNames.EventNotificationSettings.Settings)]
[TestCase(false, true, RouteNames.EventNotificationSettings.Settings)]
[TestCase(true, null, RouteNames.EventNotificationSettings.EventTypes)]
[TestCase(true, false, RouteNames.EventNotificationSettings.EventTypes)]
[TestCase(true, true, RouteNames.EventNotificationSettings.Settings)]
public void Post_RedirectsToCorrectRoute(bool newValue, bool? previousValue, string expectedRouteName)
{
var validator = new Mock<IValidator<ReceiveNotificationsSubmitModel>>();
var sessionService = new Mock<ISessionService>();
var orchestrator = new Mock<IEventNotificationSettingsOrchestrator>();

var sessionModel = new NotificationSettingsSessionModel
{
ReceiveNotifications = previousValue
};

validator.Setup(v => v.Validate(It.IsAny<ReceiveNotificationsSubmitModel>())).Returns(new ValidationResult());
sessionService.Setup(s => s.Get<NotificationSettingsSessionModel>()).Returns(sessionModel);

var controller = new ReceiveNotificationsController(validator.Object, orchestrator.Object, sessionService.Object);

var submitModel = new ReceiveNotificationsSubmitModel
{
ReceiveNotifications = newValue
};

var result = controller.Post(submitModel, CancellationToken.None).Result;

result.Should().BeOfType<RedirectToRouteResult>();
var redirectResult = (RedirectToRouteResult)result;

redirectResult.RouteName.Should().Be(expectedRouteName);
}
}
8 changes: 8 additions & 0 deletions src/SFA.DAS.ApprenticeAan.Web/Constant/EventType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace SFA.DAS.ApprenticeAan.Web.Constant;
public static class EventType
{
public const string InPerson = "InPerson";
public const string Online = "Online";
public const string Hybrid = "Hybrid";
public const string All = "All";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using FluentValidation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SFA.DAS.ApprenticeAan.Domain.Interfaces;
using SFA.DAS.ApprenticeAan.Web.Constant;
using SFA.DAS.ApprenticeAan.Web.Infrastructure;
using SFA.DAS.ApprenticeAan.Web.Models;
using SFA.DAS.ApprenticeAan.Web.Models.EventNotificationSettings;
using SFA.DAS.ApprenticeAan.Web.Models.Onboarding;
using SFA.DAS.ApprenticeAan.Web.Orchestrators;

namespace SFA.DAS.ApprenticeAan.Web.Controllers.EventNotificationSettings;

[Authorize]
[Route("event-notification-settings/event-formats", Name = RouteNames.EventNotificationSettings.EventTypes)]
public class EventTypesController : Controller
{
public const string ViewPath = "~/Views/Onboarding/SelectNotifications.cshtml";
private readonly ISessionService _sessionService;
private readonly IEventNotificationSettingsOrchestrator _settingsOrchestrator;
private readonly IValidator<SelectNotificationsSubmitModel> _validator;

public EventTypesController(ISessionService sessionService, IOuterApiClient apiClient, IValidator<SelectNotificationsSubmitModel> validator, IEventNotificationSettingsOrchestrator settingsOrchestrator)
{
_sessionService = sessionService;
_validator = validator;
_settingsOrchestrator = settingsOrchestrator;
}

[HttpGet]
public async Task<IActionResult> Get([FromRoute] string employerAccountId, CancellationToken cancellationToken)
{
var sessionModel = _sessionService.Get<NotificationSettingsSessionModel?>();

if (sessionModel == null)
{
return RedirectToRoute(RouteNames.EventNotificationSettings.Settings);
}

var model = GetViewModel(sessionModel);
return View(ViewPath, model);
}


private SelectNotificationsViewModel GetViewModel(NotificationSettingsSessionModel sessionModel)
{
var vm = new SelectNotificationsViewModel() { };

vm.EventTypes = InitializeDefaultEventTypes();
vm.BackLink = sessionModel.LastPageVisited == RouteNames.EventNotificationSettings.MonthlyNotifications ?
Url.RouteUrl(@RouteNames.EventNotificationSettings.MonthlyNotifications) :
Url.RouteUrl(@RouteNames.EventNotificationSettings.Settings);

if (sessionModel.EventTypes.Count == 3)
{
var allOption = vm.EventTypes.Single(x => x.EventType == EventType.All);
allOption.IsSelected = true;
}
else
{
foreach (var e in vm.EventTypes)
{
foreach (var ev in sessionModel.EventTypes!.Where(ev => ev.EventType.Equals(e.EventType)))
{
e.IsSelected = true;
}
}
}

return vm;
}

private List<EventTypeModel> InitializeDefaultEventTypes() => new()
{
new EventTypeModel { EventType = EventType.InPerson, IsSelected = false, Ordering = 1 },
new EventTypeModel { EventType = EventType.Online, IsSelected = false, Ordering = 2 },
new EventTypeModel { EventType = EventType.Hybrid, IsSelected = false, Ordering = 3 },
new EventTypeModel { EventType = EventType.All, IsSelected = false, Ordering = 4 }
};

private IActionResult RedirectAccordingly(List<EventTypeModel> newEvents)
{
if (newEvents.Count == 1 && newEvents.First().EventType == EventType.Online)
{
return RedirectToRoute(RouteNames.EventNotificationSettings.Settings);
}

return RedirectToRoute(RouteNames.EventNotificationSettings.NotificationLocations);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using FluentValidation;
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SFA.DAS.ApprenticeAan.Domain.Interfaces;
using SFA.DAS.ApprenticeAan.Web.Extensions;
using SFA.DAS.ApprenticeAan.Web.Infrastructure;
using SFA.DAS.ApprenticeAan.Web.Models;
using SFA.DAS.ApprenticeAan.Web.Models.EventNotificationSettings;
using SFA.DAS.ApprenticeAan.Web.Models.Shared;
using SFA.DAS.ApprenticeAan.Web.Orchestrators;

namespace SFA.DAS.ApprenticeAan.Web.Controllers.EventNotificationSettings;

[Authorize]
[Route("event-notification-settings/monthly-notifications", Name = RouteNames.EventNotificationSettings.MonthlyNotifications)]
public class ReceiveNotificationsController(
IValidator<ReceiveNotificationsSubmitModel> validator,
IEventNotificationSettingsOrchestrator settingsOrchestrator,
ISessionService sessionService) : Controller
{
public const string ViewPath = "~/Views/EventNotificationSettings/ReceiveNotifications.cshtml";

[HttpGet]
public IActionResult Get(CancellationToken cancellationToken)
{
var sessionModel = sessionService.Get<NotificationSettingsSessionModel?>();

if (sessionModel == null)
{
return RedirectToRoute(RouteNames.EventNotificationSettings.Settings);
}

var viewModel = new ReceiveNotificationsViewModel
{
BackLink = Url.RouteUrl(RouteNames.EventNotificationSettings.Settings)!,
ReceiveNotifications = sessionModel.ReceiveNotifications
};

return View(ViewPath, viewModel);
}

[HttpPost]
public async Task<IActionResult> Post(ReceiveNotificationsSubmitModel submitModel, CancellationToken cancellationToken)
{
var result = validator.Validate(submitModel);

if (!result.IsValid)
{
var model = new ReceiveNotificationsViewModel
{
ReceiveNotifications = submitModel.ReceiveNotifications,
BackLink = Url.RouteUrl(RouteNames.EventNotificationSettings.Settings)!,

};
result.AddToModelState(ModelState);
return View(ViewPath, model);
}

var memberId = sessionService.GetMemberId();
var sessionModel = sessionService.Get<NotificationSettingsSessionModel>();

var originalValue = sessionModel.ReceiveNotifications;
var newValue = submitModel.ReceiveNotifications!.Value;

if (!newValue) sessionModel.EventTypes = new List<EventTypeModel>();
if (!newValue) sessionModel.NotificationLocations = new List<NotificationLocation>();

sessionModel.ReceiveNotifications = newValue;
sessionModel.LastPageVisited = RouteNames.EventNotificationSettings.MonthlyNotifications;
sessionService.Set(sessionModel);

if (newValue != originalValue && !newValue)
{
await settingsOrchestrator.SaveSettings(memberId, sessionModel);
}

var route = newValue != originalValue && newValue
? RouteNames.EventNotificationSettings.EventTypes
: RouteNames.EventNotificationSettings.Settings;

return RedirectToRoute(route);
}
}
6 changes: 6 additions & 0 deletions src/SFA.DAS.ApprenticeAan.Web/Models/IEventTypeViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace SFA.DAS.ApprenticeAan.Web.Models;

public interface IEventTypeViewModel
{
List<EventTypeModel> EventTypes { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace SFA.DAS.ApprenticeAan.Web.Models.Onboarding;

public class SelectNotificationsViewModel : SelectNotificationsSubmitModel, IBackLink
{
public string BackLink { get; set; } = null!;
}

public class SelectNotificationsSubmitModel : IEventTypeViewModel
{
public List<EventTypeModel> EventTypes { get; set; } = new();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Mvc;

namespace SFA.DAS.ApprenticeAan.Web.Models.Shared;

public class ReceiveNotificationsViewModel : ReceiveNotificationsSubmitModel, IBackLink
{
public string BackLink { get; set; } = null!;
}

public class ReceiveNotificationsSubmitModel
{
public bool? ReceiveNotifications { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using FluentValidation;
using SFA.DAS.ApprenticeAan.Web.Models.Shared;

namespace SFA.DAS.ApprenticeAan.Web.Validators.Onboarding;

public class ReceiveNotificationsSubmitModelValidator : AbstractValidator<ReceiveNotificationsSubmitModel>
{
public const string NoSelectionErrorMessage = "Select if you want to receive a monthly email about upcoming events";

public ReceiveNotificationsSubmitModelValidator()
{
RuleFor(m => m.ReceiveNotifications)
.NotNull()
.WithMessage(NoSelectionErrorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
@using SFA.DAS.ApprenticeAan.Web.Models.Shared
@model ReceiveNotificationsViewModel
@{
ViewData["Title"] = "Do you want to get a monthly email about upcoming events?";
}
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<partial name="_ValidationSummary" />
<form method="post">
<fieldset class="govuk-fieldset">
<div esfa-validation-marker-for="ReceiveNotifications" class="govuk-form-group">
<legend class="govuk-fieldset__legend govuk-fieldset__legend--l">
<h1 class="govuk-heading-l">
Do you want to get a monthly email about upcoming events?
</h1>
</legend>

<p class="govuk-hint">
We'll email you at the start of each month
</p>

<span asp-validation-for="ReceiveNotifications" class="govuk-error-message"></span>
<div id="ReceiveNotifications-options" class="govuk-radios" data-module="govuk-radios">
<div class="govuk-radios__item">
<input asp-for="ReceiveNotifications" class="govuk-radios__input" type="radio" value="true"
sfa-validationerror-class="form-control-error">
<label class="govuk-label govuk-radios__label" for="ReceiveNotifications">
Yes
</label>
</div>
<div class="govuk-radios__item">
<input asp-for="ReceiveNotifications" id="No" class="govuk-radios__input" type="radio" value="false"
sfa-validationerror-class="form-control-error">
<label class="govuk-label govuk-radios__label" for="No">
No
</label>
</div>
</div>
</div>
</fieldset>
<div class="govuk-button-group">
<button id="continue" type="submit" class="govuk-button" data-module="govuk-button" data-disable-on-submit="true">
Continue
</button>
</div>
</form>
</div>
</div>
Loading

0 comments on commit 85df8f4

Please sign in to comment.