diff --git a/CHANGELOG.md b/CHANGELOG.md index 745139dbe..05379d02b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased][unreleased] +### Added + +- Created Edit contacts UI and feature flag to hide it on production environment + ### Changed - Ofsted logic can now handle null dates +- Updated contacts page UI to split each contact into it own section ## [Release-6][release-6] (production-2024-09-17.3129) diff --git a/DfE.FindInformationAcademiesTrusts/Configuration/FeatureFlags.cs b/DfE.FindInformationAcademiesTrusts/Configuration/FeatureFlags.cs index 8e22a4f8b..edecbc7f8 100644 --- a/DfE.FindInformationAcademiesTrusts/Configuration/FeatureFlags.cs +++ b/DfE.FindInformationAcademiesTrusts/Configuration/FeatureFlags.cs @@ -3,4 +3,5 @@ public static class FeatureFlags { public const string TestFlag = "TestFlag"; + public const string EditContactsUI = "EditContactsUI"; } diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Search.cshtml.cs b/DfE.FindInformationAcademiesTrusts/Pages/Search.cshtml.cs index 524b371f9..4ccbff1f1 100644 --- a/DfE.FindInformationAcademiesTrusts/Pages/Search.cshtml.cs +++ b/DfE.FindInformationAcademiesTrusts/Pages/Search.cshtml.cs @@ -49,7 +49,7 @@ public async Task OnGetAsync() Trusts = await GetTrustsForKeywords(); PageStatus = Trusts.PageStatus; - PaginationRouteData = new Dictionary { { "Keywords", KeyWords } }; + PaginationRouteData = new Dictionary { { "Keywords", KeyWords ?? string.Empty } }; return new PageResult(); } diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Shared/BasePageModel.cs b/DfE.FindInformationAcademiesTrusts/Pages/Shared/BasePageModel.cs index 0b77e1720..418732a67 100644 --- a/DfE.FindInformationAcademiesTrusts/Pages/Shared/BasePageModel.cs +++ b/DfE.FindInformationAcademiesTrusts/Pages/Shared/BasePageModel.cs @@ -6,5 +6,5 @@ namespace DfE.FindInformationAcademiesTrusts.Pages.Shared; public abstract class BasePageModel : PageModel { public bool ShowHeaderSearch { get; init; } = true; - [BindProperty(SupportsGet = true)] public string KeyWords { get; set; } = string.Empty; + [BindProperty(SupportsGet = true)] public string? KeyWords { get; set; } = string.Empty; } diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Shared/IPageSearchFormModel.cs b/DfE.FindInformationAcademiesTrusts/Pages/Shared/IPageSearchFormModel.cs index c25ff7f32..caa1e88ab 100644 --- a/DfE.FindInformationAcademiesTrusts/Pages/Shared/IPageSearchFormModel.cs +++ b/DfE.FindInformationAcademiesTrusts/Pages/Shared/IPageSearchFormModel.cs @@ -2,6 +2,6 @@ namespace DfE.FindInformationAcademiesTrusts.Pages.Shared; public interface IPageSearchFormModel { - string KeyWords { get; set; } + string? KeyWords { get; set; } string PageSearchFormInputId { get; } } diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Shared/_ErrorSummary.cshtml b/DfE.FindInformationAcademiesTrusts/Pages/Shared/_ErrorSummary.cshtml new file mode 100644 index 000000000..80c3b4606 --- /dev/null +++ b/DfE.FindInformationAcademiesTrusts/Pages/Shared/_ErrorSummary.cshtml @@ -0,0 +1,25 @@ +@model Microsoft.AspNetCore.Mvc.RazorPages.PageModel + +@if (!Model.ModelState.IsValid) +{ +
+
+

+ There is a problem +

+
+
    + @foreach (var (key, modelState) in Model.ModelState) + { + @foreach (var error in modelState.Errors) + { +
  • + @error.ErrorMessage +
  • + } + } +
+
+
+
+} diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts.cshtml b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts.cshtml index 3ad33d852..3d8f074ac 100644 --- a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts.cshtml +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts.cshtml @@ -1,4 +1,5 @@ @page +@using DfE.FindInformationAcademiesTrusts.Configuration @using DfE.FindInformationAcademiesTrusts.Data @model ContactsModel @@ -6,58 +7,82 @@ Layout = "_TrustLayout"; } -
-
+
+

+ Contacts at DfE +

+ + @if (!string.IsNullOrWhiteSpace(TempData["ContactUpdatedMessage"] as string)) + { + + } + +
-

DfE contacts

-
-
-
-
-
- Trust relationship manager -
- @{ DisplayContact(Model.TrustRelationshipManager); } +

+ Trust relationship manager +

+ + -
-
- SFSO (Schools financial support and oversight) lead -
- @{ DisplayContact(Model.SfsoLead); } + +
+ @{ DisplayContact(Model.TrustRelationshipManager); } +
+
+
+

+ SFSO (Schools financial support and oversight) lead +

+ + -
+
+ @{ DisplayContact(Model.SfsoLead); }
-
+

+ Contacts at the trust +

+
-

Trust contacts

+

+ Accounting officer +

-
-
-
-
- Accounting officer -
- @{ DisplayContact(Model.AccountingOfficer); } -
-
-
- Chair of trustees -
- @{ DisplayContact(Model.ChairOfTrustees); } -
-
-
- Chief financial officer -
- @{ DisplayContact(Model.ChiefFinancialOfficer); } -
- -
+ @{ DisplayContact(Model.AccountingOfficer); } +
+
+
+

+ Chair of trustees +

+
+ @{ DisplayContact(Model.ChairOfTrustees); } +
+
+
+

+ Chief financial officer +

+ @{ DisplayContact(Model.ChiefFinancialOfficer); }
@@ -65,35 +90,38 @@ private void DisplayContact(Person? contactToDisplay) { -
- - @if (contactToDisplay is null) - { -

@ContactsModel.ContactInformationNotAvailableMessage

- } - else - { - @if (!string.IsNullOrWhiteSpace(contactToDisplay.FullName)) - { -

@contactToDisplay.FullName

- } - else - { -

@ContactsModel.ContactNameNotAvailableMessage

- } - -

- @if (contactToDisplay.Email is not null) +

+
+
+
+ Name +
+ @if (!string.IsNullOrWhiteSpace(contactToDisplay?.FullName)) + { +
@contactToDisplay.FullName
+ } + else + { +
@ContactsModel.ContactNameNotAvailableMessage
+ } +
+
+
+ Email address +
+ @if (contactToDisplay?.Email is not null) { - @contactToDisplay.Email +
+ @contactToDisplay.Email +
} else { - @ContactsModel.ContactEmailNotAvailableMessage +
@ContactsModel.ContactEmailNotAvailableMessage
} -

- } -
+
+ + } } diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts.cshtml.cs b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts.cshtml.cs index 3b4c41f10..103e7f522 100644 --- a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts.cshtml.cs +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts.cshtml.cs @@ -23,8 +23,6 @@ public class ContactsModel( public const string ContactEmailNotAvailableMessage = "No contact email available"; - public const string ContactInformationNotAvailableMessage = "No contact information available"; - public override async Task OnGetAsync() { var pageResult = await base.OnGetAsync(); diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditContactModel.cs b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditContactModel.cs new file mode 100644 index 000000000..9177f26d4 --- /dev/null +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditContactModel.cs @@ -0,0 +1,69 @@ +using System.ComponentModel.DataAnnotations; +using DfE.FindInformationAcademiesTrusts.Data; +using DfE.FindInformationAcademiesTrusts.Services.DataSource; +using DfE.FindInformationAcademiesTrusts.Services.Trust; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace DfE.FindInformationAcademiesTrusts.Pages.Trusts.Contacts; + +public abstract class EditContactModel( + ITrustProvider trustProvider, + IDataSourceService dataSourceService, + ITrustService trustService, + ILogger logger, + string roleText) + : TrustsAreaModel(trustProvider, dataSourceService, trustService, + logger, $"Edit {roleText}") +{ + public const string NameField = "Name"; + public const string EmailField = "Email"; + + [BindProperty] [BindRequired] public string? Name { get; set; } + + [BindProperty] + [BindRequired] + [EmailAddress(ErrorMessage = "Enter an email address in the correct format, like name@education.gov.uk")] + [RegularExpression(".*@education.gov.uk$", ErrorMessage = "Enter a DfE email address ending in @education.gov.uk")] + public string? Email { get; set; } + + [TempData] public string ContactUpdatedMessage { get; set; } = string.Empty; + + private string RoleText { get; init; } = roleText; + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return await base.OnGetAsync(); + } + + ContactUpdatedMessage = $"Changes made to the {RoleText} were successfully updated."; + return RedirectToPage("/Trusts/Contacts", new { Uid }); + } + + public string GetErrorClass(string key) + { + return ModelState.ContainsKey(key) && ModelState[key]!.Errors.Any() ? "govuk-form-group--error" : string.Empty; + } + + public string GenerateErrorAriaDescribedBy(string key) + { + return string.Join(" ", GetErrorList(key).Select(value => $"error-{key}-{value.index}")); + } + + public (int index, string errorMessage)[] GetErrorList(string key) + { + if (ModelState.ContainsKey(key)) + { + return ModelState[key]!.Errors.Select((error, index) => (index, error.ErrorMessage)).ToArray(); + } + + return []; + } + + public string GeneratePageTitle() + { + return $"{(ModelState.IsValid ? string.Empty : "Error: ")}{PageTitle ?? PageName} - {TrustSummary.Name}"; + } +} diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditSfsoLead.cshtml b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditSfsoLead.cshtml new file mode 100644 index 000000000..0eec67e12 --- /dev/null +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditSfsoLead.cshtml @@ -0,0 +1,9 @@ +@page +@model EditSfsoLeadModel + +@{ + Layout = "_Layout"; + ViewData["Title"] = Model.GeneratePageTitle(); +} + + diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditSfsoLead.cshtml.cs b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditSfsoLead.cshtml.cs new file mode 100644 index 000000000..09afd602b --- /dev/null +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditSfsoLead.cshtml.cs @@ -0,0 +1,31 @@ +using DfE.FindInformationAcademiesTrusts.Configuration; +using DfE.FindInformationAcademiesTrusts.Data; +using DfE.FindInformationAcademiesTrusts.Services.DataSource; +using DfE.FindInformationAcademiesTrusts.Services.Trust; +using Microsoft.AspNetCore.Mvc; +using Microsoft.FeatureManagement.Mvc; + +namespace DfE.FindInformationAcademiesTrusts.Pages.Trusts.Contacts; + +[FeatureGate(FeatureFlags.EditContactsUI)] +public class EditSfsoLeadModel( + ITrustProvider trustProvider, + IDataSourceService dataSourceService, + ILogger logger, + ITrustService trustService) + : EditContactModel(trustProvider, dataSourceService, trustService, + logger, "SFSO (Schools financial support and oversight) lead") +{ + public override async Task OnGetAsync() + { + var pageResult = await base.OnGetAsync(); + + if (pageResult.GetType() == typeof(NotFoundResult)) return pageResult; + + var contacts = await TrustService.GetTrustContactsAsync(Uid); + + Email = contacts.SfsoLead?.Email; + Name = contacts.SfsoLead?.FullName; + return pageResult; + } +} diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditTrustRelationshipManager.cshtml b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditTrustRelationshipManager.cshtml new file mode 100644 index 000000000..8c3417ee3 --- /dev/null +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditTrustRelationshipManager.cshtml @@ -0,0 +1,9 @@ +@page +@model EditTrustRelationshipManagerModel + +@{ + Layout = "_Layout"; + ViewData["Title"] = Model.GeneratePageTitle(); +} + + diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditTrustRelationshipManager.cshtml.cs b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditTrustRelationshipManager.cshtml.cs new file mode 100644 index 000000000..bc3837833 --- /dev/null +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/EditTrustRelationshipManager.cshtml.cs @@ -0,0 +1,32 @@ +using DfE.FindInformationAcademiesTrusts.Configuration; +using DfE.FindInformationAcademiesTrusts.Data; +using DfE.FindInformationAcademiesTrusts.Services.DataSource; +using DfE.FindInformationAcademiesTrusts.Services.Trust; +using Microsoft.AspNetCore.Mvc; +using Microsoft.FeatureManagement.Mvc; + +namespace DfE.FindInformationAcademiesTrusts.Pages.Trusts.Contacts; + +[FeatureGate(FeatureFlags.EditContactsUI)] +public class EditTrustRelationshipManagerModel( + ITrustProvider trustProvider, + IDataSourceService dataSourceService, + ILogger logger, + ITrustService trustService) + : EditContactModel(trustProvider, dataSourceService, trustService, + logger, "Trust relationship manager") +{ + public override async Task OnGetAsync() + { + var pageResult = await base.OnGetAsync(); + + if (pageResult.GetType() == typeof(NotFoundResult)) return pageResult; + + var contacts = await TrustService.GetTrustContactsAsync(Uid); + + Email = contacts.TrustRelationshipManager?.Email; + Name = contacts.TrustRelationshipManager?.FullName; + + return pageResult; + } +} diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/_EditContactsForm.cshtml b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/_EditContactsForm.cshtml new file mode 100644 index 000000000..8fe045b99 --- /dev/null +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts/_EditContactsForm.cshtml @@ -0,0 +1,62 @@ +@model EditContactModel + + + +
+
+ +
+
+
+
+
+
+ @Model.Section + +

@Model.PageName

+
+
+ +
+ + @foreach (var error in Model.GetErrorList(EditContactModel.NameField)) + { +

+ Error: @error.errorMessage +

+ } + +
+
+ +
+ Only valid DfE email addresses can be used e.g. joe.bloggs@education.gov.uk +
+ @foreach (var error in Model.GetErrorList(EditContactModel.EmailField)) + { +

+ Error: @error.errorMessage +

+ } + +
+
+ + + Cancel + +
+
+
+ +
+
+
+
+
diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/ITrustsAreaModel.cs b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/ITrustsAreaModel.cs index 68dd73a9f..b8db1f45a 100644 --- a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/ITrustsAreaModel.cs +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/ITrustsAreaModel.cs @@ -17,7 +17,7 @@ public interface ITrustsAreaModel /// /// The name of the page as displayed in the browser title /// - string? PageTitle { get; init; } + string? PageTitle { get; set; } /// /// The name of the section the page sits under in side navigation diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/TrustsAreaModel.cs b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/TrustsAreaModel.cs index 513b95bb0..5b0e92b47 100644 --- a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/TrustsAreaModel.cs +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/TrustsAreaModel.cs @@ -23,7 +23,7 @@ public class TrustsAreaModel( public TrustSummaryServiceModel TrustSummary { get; set; } = default!; public List DataSources { get; set; } = []; public string PageName { get; init; } = pageName; - public string? PageTitle { get; init; } + public string? PageTitle { get; set; } public string Section => ViewConstants.AboutTheTrustSectionName; public string MapDataSourceToName(DataSourceServiceModel dataSource) diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/_TrustBanner.cshtml b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/_TrustBanner.cshtml new file mode 100644 index 000000000..63455d93c --- /dev/null +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/_TrustBanner.cshtml @@ -0,0 +1,12 @@ +@model ITrustsAreaModel + + diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/_TrustBreadcrumbs.cshtml b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/_TrustBreadcrumbs.cshtml new file mode 100644 index 000000000..7b2a8b36f --- /dev/null +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/_TrustBreadcrumbs.cshtml @@ -0,0 +1,16 @@ +@model ITrustsAreaModel + +
+
+ +
+
diff --git a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/_TrustLayout.cshtml b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/_TrustLayout.cshtml index f09857926..92ee7c0ef 100644 --- a/DfE.FindInformationAcademiesTrusts/Pages/Trusts/_TrustLayout.cshtml +++ b/DfE.FindInformationAcademiesTrusts/Pages/Trusts/_TrustLayout.cshtml @@ -5,33 +5,11 @@ ViewData["Title"] = $"{Model.PageTitle ?? Model.PageName} - {Model.TrustSummary.Name}"; } - +
-
-
- -
-
+
diff --git a/DfE.FindInformationAcademiesTrusts/appsettings.json b/DfE.FindInformationAcademiesTrusts/appsettings.json index 474335d6c..a330f3e22 100644 --- a/DfE.FindInformationAcademiesTrusts/appsettings.json +++ b/DfE.FindInformationAcademiesTrusts/appsettings.json @@ -39,7 +39,8 @@ } }, "FeatureManagement": { - "TestFlag": false + "TestFlag": false, + "EditContactsUI": false }, "DataProtection": { "KeyVaultKey": "", diff --git a/docs/feature-flags.md b/docs/feature-flags.md index 7d7989d7b..db705e62a 100644 --- a/docs/feature-flags.md +++ b/docs/feature-flags.md @@ -5,6 +5,7 @@ Feature flags can be used to programatically turn features on and off. We can us ## List of current flags - `TestFlag` (Default: false): Flag added to test the functionality of feature flags. Adds a line to the layout that shows flags are working when set to true. +- `EditContactsUI` (Default: false): Flag added to control who can see the edit contacts UI on the Trust contacts page while that is still in development. ## Implementation diff --git a/tests/DFE.FindInformationAcademiesTrusts.CypressTests/cypress/pages/trustContactsPage.ts b/tests/DFE.FindInformationAcademiesTrusts.CypressTests/cypress/pages/trustContactsPage.ts index 9aad31897..1674a034f 100644 --- a/tests/DFE.FindInformationAcademiesTrusts.CypressTests/cypress/pages/trustContactsPage.ts +++ b/tests/DFE.FindInformationAcademiesTrusts.CypressTests/cypress/pages/trustContactsPage.ts @@ -40,7 +40,11 @@ class TrustContacts { private assertEmptyContact(id: string): void { cy.getByTestId(id) - .find("p").should("contain.text", "No contact information available"); + .find("[data-testid='contact-name']").should("contain.text", "No contact name available"); + + cy.getByTestId(id) + .find("[data-testid='contact-email']") + .should("contain.text", "No contact email available"); } private assertContact(id: string, name: string, email: string): void { diff --git a/tests/DfE.FindInformationAcademiesTrusts.UnitTests/Pages/Trusts/Contacts/EditContactModelTests.cs b/tests/DfE.FindInformationAcademiesTrusts.UnitTests/Pages/Trusts/Contacts/EditContactModelTests.cs new file mode 100644 index 000000000..25e40cd2e --- /dev/null +++ b/tests/DfE.FindInformationAcademiesTrusts.UnitTests/Pages/Trusts/Contacts/EditContactModelTests.cs @@ -0,0 +1,136 @@ +using DfE.FindInformationAcademiesTrusts.Data; +using DfE.FindInformationAcademiesTrusts.Pages.Trusts.Contacts; +using DfE.FindInformationAcademiesTrusts.Services.Trust; +using DfE.FindInformationAcademiesTrusts.UnitTests.Mocks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace DfE.FindInformationAcademiesTrusts.UnitTests.Pages.Trusts.Contacts; + +public class EditContactModelTests +{ + private EditContactModel _sut; + private readonly Mock _mockTrustProvider = new(); + private readonly MockDataSourceService _mockDataSourceService = new(); + private readonly Mock _mockTrustService = new(); + + private readonly TrustSummaryServiceModel _fakeTrust = new("1234", "My Trust", "Multi-academy trust", 3); + + public EditContactModelTests() + { + _mockTrustService.Setup(tp => tp.GetTrustContactsAsync("1234")).ReturnsAsync( + new TrustContactsServiceModel(null, null, null, null, null)); + _mockTrustService.Setup(t => t.GetTrustSummaryAsync(_fakeTrust.Uid)) + .ReturnsAsync(_fakeTrust); + + _sut = new EditSfsoLeadModel(_mockTrustProvider.Object, _mockDataSourceService.Object, + new MockLogger().Object, _mockTrustService.Object) + { Uid = "1234" }; + } + + [Fact] + public async Task OnPostAsync_sets_ContactUpdated_to_true_when_validation_is_correct() + { + _sut.TrustSummary = _fakeTrust; + var result = await _sut.OnPostAsync(); + result.Should().BeOfType(); + _sut.ContactUpdatedMessage.Should() + .Be("Changes made to the SFSO (Schools financial support and oversight) lead were successfully updated."); + var redirect = result as RedirectToPageResult; + redirect!.PageName.Should().Be("/Trusts/Contacts"); + _sut.GeneratePageTitle().Should().NotContain("Error: "); + } + + [Fact] + public async Task OnPostAsync_sets_ContactUpdated_to_false_when_validation_is_incorrect() + { + _sut.ModelState.AddModelError("Test", "Test"); + var result = await _sut.OnPostAsync(); + result.Should().BeOfType(); + _sut.GeneratePageTitle().Should().Contain("Error: "); + } + + [Fact] + public void GetErrorClass_returns_the_class_string_when_validation_is_incorrect() + { + _sut.ModelState.AddModelError("Test", "Test"); + var result = _sut.GetErrorClass("Test"); + result.Should().Be("govuk-form-group--error"); + } + + [Theory] + [InlineData("")] + [InlineData("Name")] + [InlineData("Email")] + public void GetErrorClass_returns_empty_string_when_validation_is_correct(string key) + { + var result = _sut.GetErrorClass(key); + result.Should().Be(string.Empty); + } + + [Fact] + public void GenerateErrorAriaDescribedBy_returns_the_correct_string_when_validation_is_incorrect() + { + _sut.ModelState.AddModelError("Test", "Test"); + var result = _sut.GenerateErrorAriaDescribedBy("Test"); + result.Should().Be("error-Test-0"); + } + + [Fact] + public void GenerateErrorAriaDescribedBy_returns_empty_when_validation_is_correct() + { + var result = _sut.GenerateErrorAriaDescribedBy("Test"); + result.Should().Be(string.Empty); + } + + [Fact] + public void GetErrorList_returns_the_correct_list_when_validation_is_incorrect() + { + _sut.ModelState.AddModelError("Test", "Test"); + var result = _sut.GetErrorList("Test"); + result.Should().HaveCount(1); + result.Should().BeEquivalentTo([(0, "Test")]); + } + + [Fact] + public void GetErrorList_returns_an_empty_list_when_validation_is_correct() + { + var result = _sut.GetErrorList("Test"); + result.Should().HaveCount(0); + result.Should().BeEquivalentTo(Array.Empty<(int, string)>()); + } + + [Theory] + [InlineData("title", null, null, "title - My Trust")] + [InlineData("title", "name", null, "title - My Trust")] + [InlineData(null, "name", null, "name - My Trust")] + [InlineData("title", null, "error", "Error: title - My Trust")] + [InlineData("title", "name", "error", "Error: title - My Trust")] + [InlineData(null, "name", "error", "Error: name - My Trust")] + public void GeneratePageTitle_returns_correctly_(string? pageTitle, string? pageName, string? error, + string expected) + { + if (pageName != null) + { + _sut = new EditSfsoLeadModel(_mockTrustProvider.Object, _mockDataSourceService.Object, + new MockLogger().Object, _mockTrustService.Object) + { Uid = "1234", PageName = pageName, PageTitle = pageTitle }; + } + else + { + _sut = new EditSfsoLeadModel(_mockTrustProvider.Object, _mockDataSourceService.Object, + new MockLogger().Object, _mockTrustService.Object) + { Uid = "1234", PageTitle = pageTitle }; + } + + _sut.TrustSummary = _fakeTrust; + + if (error is not null) + { + _sut.ModelState.AddModelError(error, error); + } + + var result = _sut.GeneratePageTitle(); + result.Should().Be(expected); + } +} diff --git a/tests/DfE.FindInformationAcademiesTrusts.UnitTests/Pages/Trusts/Contacts/EditSfsoLeadModelTests.cs b/tests/DfE.FindInformationAcademiesTrusts.UnitTests/Pages/Trusts/Contacts/EditSfsoLeadModelTests.cs new file mode 100644 index 000000000..774ce3a04 --- /dev/null +++ b/tests/DfE.FindInformationAcademiesTrusts.UnitTests/Pages/Trusts/Contacts/EditSfsoLeadModelTests.cs @@ -0,0 +1,56 @@ +using DfE.FindInformationAcademiesTrusts.Data; +using DfE.FindInformationAcademiesTrusts.Pages.Trusts.Contacts; +using DfE.FindInformationAcademiesTrusts.Services.Trust; +using DfE.FindInformationAcademiesTrusts.UnitTests.Mocks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace DfE.FindInformationAcademiesTrusts.UnitTests.Pages.Trusts.Contacts; + +public class EditSfsoLeadModelTests +{ + private readonly EditSfsoLeadModel _sut; + private readonly Mock _mockTrustProvider = new(); + + private readonly MockDataSourceService _mockDataSourceService = new(); + private readonly Mock _mockTrustService = new(); + + private readonly TrustSummaryServiceModel _fakeTrust = new("1234", "My Trust", "Multi-academy trust", 3); + + private readonly Person _sfsoLead = new("Sfso Lead", "sfso.lead@test.com"); + + public EditSfsoLeadModelTests() + { + _mockTrustService.Setup(tp => tp.GetTrustContactsAsync("1234")).ReturnsAsync( + new TrustContactsServiceModel(null, _sfsoLead, null, null, null)); + _mockTrustService.Setup(t => t.GetTrustSummaryAsync(_fakeTrust.Uid)) + .ReturnsAsync(_fakeTrust); + + _sut = new EditSfsoLeadModel(_mockTrustProvider.Object, _mockDataSourceService.Object, + new MockLogger().Object, _mockTrustService.Object) + { Uid = "1234" }; + } + + [Fact] + public void PageName_should_be_correct() + { + _sut.PageName.Should().Be("Edit SFSO (Schools financial support and oversight) lead"); + } + + [Fact] + public async Task OnGetAsync_returns_NotFoundResult_if_Trust_is_not_found() + { + _mockTrustService.Setup(r => r.GetTrustSummaryAsync("1234")).ReturnsAsync((TrustSummaryServiceModel?)null); + var result = await _sut.OnGetAsync(); + result.Should().BeOfType(); + } + + [Fact] + public async Task OnGetAsync_loads_the_correct_name_and_email() + { + var result = await _sut.OnGetAsync(); + result.Should().BeOfType(); + _sut.Name.Should().Be(_sfsoLead.FullName); + _sut.Email.Should().Be(_sfsoLead.Email); + } +} diff --git a/tests/DfE.FindInformationAcademiesTrusts.UnitTests/Pages/Trusts/Contacts/EditTrustRelationshipManagerModelTests.cs b/tests/DfE.FindInformationAcademiesTrusts.UnitTests/Pages/Trusts/Contacts/EditTrustRelationshipManagerModelTests.cs new file mode 100644 index 000000000..a8a95aaf9 --- /dev/null +++ b/tests/DfE.FindInformationAcademiesTrusts.UnitTests/Pages/Trusts/Contacts/EditTrustRelationshipManagerModelTests.cs @@ -0,0 +1,56 @@ +using DfE.FindInformationAcademiesTrusts.Data; +using DfE.FindInformationAcademiesTrusts.Pages.Trusts.Contacts; +using DfE.FindInformationAcademiesTrusts.Services.Trust; +using DfE.FindInformationAcademiesTrusts.UnitTests.Mocks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace DfE.FindInformationAcademiesTrusts.UnitTests.Pages.Trusts.Contacts; + +public class EditTrustRelationshipManagerModelTests +{ + private readonly EditTrustRelationshipManagerModel _sut; + private readonly Mock _mockTrustProvider = new(); + + private readonly MockDataSourceService _mockDataSourceService = new(); + private readonly Mock _mockTrustService = new(); + + private readonly TrustSummaryServiceModel _fakeTrust = new("1234", "My Trust", "Multi-academy trust", 3); + + private readonly Person _trustRelationshipManager = new("Trust Relationship Manager", "trm@test.com"); + + public EditTrustRelationshipManagerModelTests() + { + _mockTrustService.Setup(tp => tp.GetTrustContactsAsync("1234")).ReturnsAsync( + new TrustContactsServiceModel(_trustRelationshipManager, null, null, null, null)); + _mockTrustService.Setup(t => t.GetTrustSummaryAsync(_fakeTrust.Uid)) + .ReturnsAsync(_fakeTrust); + + _sut = new EditTrustRelationshipManagerModel(_mockTrustProvider.Object, _mockDataSourceService.Object, + new MockLogger().Object, _mockTrustService.Object) + { Uid = "1234" }; + } + + [Fact] + public void PageName_should_be_correct() + { + _sut.PageName.Should().Be("Edit Trust relationship manager"); + } + + [Fact] + public async Task OnGetAsync_returns_NotFoundResult_if_Trust_is_not_found() + { + _mockTrustService.Setup(r => r.GetTrustSummaryAsync("1234")).ReturnsAsync((TrustSummaryServiceModel?)null); + var result = await _sut.OnGetAsync(); + result.Should().BeOfType(); + } + + [Fact] + public async Task OnGetAsync_loads_the_correct_name_and_email() + { + var result = await _sut.OnGetAsync(); + result.Should().BeOfType(); + _sut.Name.Should().Be(_trustRelationshipManager.FullName); + _sut.Email.Should().Be(_trustRelationshipManager.Email); + } +}