-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial booking questions implementation
- Loading branch information
1 parent
57efb80
commit 53c7526
Showing
100 changed files
with
7,099 additions
and
170 deletions.
There are no files selected for viewing
67 changes: 67 additions & 0 deletions
67
src/ServiceAssessmentService/ServiceAssessmentService.WebApp.Test/BasicWebTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using FluentAssertions; | ||
using FluentAssertions.Execution; | ||
using Microsoft.AspNetCore.Mvc.Testing; | ||
using Xunit; | ||
|
||
namespace ServiceAssessmentService.WebApp.Test; | ||
|
||
public class BasicWebTests : IClassFixture<WebApplicationFactory<Program>> | ||
{ | ||
private readonly WebApplicationFactory<Program> _factory; | ||
|
||
public BasicWebTests(WebApplicationFactory<Program> factory) | ||
{ | ||
_factory = factory; | ||
} | ||
|
||
public static IEnumerable<object[]> SimpleUrls => | ||
new List<object[]> | ||
{ | ||
new object[] { "/" }, | ||
new object[] { "/AccessibilityStatement" }, | ||
new object[] { "/CookiePolicy" }, | ||
new object[] { "/Privacy" }, | ||
new object[] { "/Dashboard" }, | ||
new object[] { "/Book/BookingRequest/Index" }, | ||
// new object[] { "/Book/Request/AssessmentType/Index" }, | ||
}; | ||
|
||
[Theory] | ||
[MemberData(nameof(SimpleUrls))] | ||
public async Task StatusCodeMustIndicateSuccessResponse(string url) | ||
{ | ||
// Arrange | ||
var client = _factory.CreateClient(); | ||
|
||
// Act | ||
var response = await client.GetAsync(url); | ||
|
||
// Assert | ||
using (new AssertionScope()) | ||
{ | ||
response.IsSuccessStatusCode.Should().BeTrue(); // Status Code 200-299 | ||
|
||
// Redundant additional checks, but provides extra information in case of failure. | ||
int statusCode = (int)response.StatusCode; | ||
statusCode.Should().BeGreaterThanOrEqualTo(200); | ||
statusCode.Should().BeLessThanOrEqualTo(299); | ||
|
||
response.StatusCode.Should().Be(System.Net.HttpStatusCode.OK); | ||
} | ||
} | ||
|
||
[Theory] | ||
[MemberData(nameof(SimpleUrls))] | ||
public async Task ContentTypeMustBeTextHtmlUtf8(string url) | ||
{ | ||
// Arrange | ||
var client = _factory.CreateClient(); | ||
|
||
// Act | ||
var response = await client.GetAsync(url); | ||
|
||
// Assert | ||
response.Content?.Headers?.ContentType?.ToString() | ||
.Should().Be("text/html; charset=utf-8"); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
.../ServiceAssessmentService.WebApp.Test/Book/FakeNotImplementedBookingRequestReadService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using ServiceAssessmentService.WebApp.Core; | ||
using ServiceAssessmentService.WebApp.Models; | ||
using System.Diagnostics.CodeAnalysis; | ||
using ServiceAssessmentService.WebApp.Services.Book; | ||
|
||
namespace ServiceAssessmentService.WebApp.Test.Book; | ||
|
||
/// <summary> | ||
/// Stub fake booking request read service. | ||
/// All implementations of interface methods throw NotImplementedException, | ||
/// and are virtual so that they may be overridden as required. | ||
/// </summary> | ||
[ExcludeFromCodeCoverage] | ||
public class FakeNotImplementedBookingRequestReadService : IBookingRequestReadService | ||
{ | ||
public Task<IEnumerable<IncompleteBookingRequest>> GetAllAssessments() | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public virtual Task<IncompleteBookingRequest?> GetByIdAsync(BookingRequestId id) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public Task<List<DateOnly>> AvailableReviewDates(BookingRequestId id) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public virtual Task<IEnumerable<Phase>> GetPhases() | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
...ServiceAssessmentService.WebApp.Test/Book/FakeNotImplementedBookingRequestWriteService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
using ServiceAssessmentService.WebApp.Core; | ||
using ServiceAssessmentService.WebApp.Models; | ||
using System.Diagnostics.CodeAnalysis; | ||
using ServiceAssessmentService.WebApp.Services.Book; | ||
|
||
namespace ServiceAssessmentService.WebApp.Test.Book; | ||
|
||
/// <summary> | ||
/// Stub fake booking request write service. | ||
/// All implementations of interface methods throw NotImplementedException, | ||
/// and are virtual so that they may be overridden as required. | ||
/// </summary> | ||
[ExcludeFromCodeCoverage] | ||
public class FakeNotImplementedBookingRequestWriteService : IBookingRequestWriteService | ||
{ | ||
|
||
public virtual Task<IncompleteBookingRequest> CreateRequestAsync(Phase phaseConcluding, AssessmentType assessmentType) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public virtual Task<ChangeRequestModel> UpdateRequestName(BookingRequestId id, string proposedName) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public virtual Task<ChangeRequestModel> UpdateDescription(BookingRequestId id, string proposedDescription) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public virtual Task<ChangeRequestModel> UpdateProjectCode(BookingRequestId id, bool? isProjectCodeKnown, | ||
string proposedProjectCode) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public Task<ChangeRequestModel> UpdateStartDate(BookingRequestId id, string? proposedYear, string? proposedMonth, string? proposedDayOfMonth) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public Task<ChangeRequestModel> UpdateEndDate(BookingRequestId id, bool? isEndDateKnown, string? proposedYear, string? proposedMonth, | ||
string? proposedDayOfMonth) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public Task<ChangeRequestModel> UpdateReviewDates(BookingRequestId id, List<DateOnly> proposedReviewDates) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public Task<ChangeRequestModel> UpdateReviewDate(BookingRequestId bookingRequestId, DateOnly? proposedReviewDate) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
...Service/ServiceAssessmentService.WebApp.Test/Book/FakeNotImplementedLookupsReadService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
using ServiceAssessmentService.WebApp.Models; | ||
using System.Diagnostics.CodeAnalysis; | ||
using ServiceAssessmentService.WebApp.Services.Lookups; | ||
|
||
namespace ServiceAssessmentService.WebApp.Test.Book; | ||
|
||
/// <summary> | ||
/// Stub fake booking request read service. | ||
/// All implementations of interface methods throw NotImplementedException, | ||
/// and are virtual so that they may be overridden as required. | ||
/// </summary> | ||
[ExcludeFromCodeCoverage] | ||
public class FakeNotImplementedLookupsReadService : ILookupsReadService | ||
{ | ||
public virtual Task<IEnumerable<Phase>> GetPhases() | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public virtual Task<IEnumerable<AssessmentType>> GetAssessmentTypes() | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
} |
117 changes: 117 additions & 0 deletions
117
...vice/ServiceAssessmentService.WebApp.Test/Book/Request/QuestionAssessmentTypePostTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
using AngleSharp.Dom; | ||
using AngleSharp.Html.Dom; | ||
using FluentAssertions; | ||
using FluentAssertions.Execution; | ||
using Microsoft.AspNetCore.Mvc.Testing; | ||
using Microsoft.AspNetCore.TestHost; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using ServiceAssessmentService.WebApp.Controllers.Book; | ||
using ServiceAssessmentService.WebApp.Models; | ||
using ServiceAssessmentService.WebApp.Services.Lookups; | ||
using ServiceAssessmentService.WebApp.Test.TestHelpers; | ||
using Xunit; | ||
|
||
namespace ServiceAssessmentService.WebApp.Test.Book.Request; | ||
|
||
public class QuestionAssessmentTypePostTests : IClassFixture<WebApplicationFactory<Program>> | ||
{ | ||
private const string Url = "/Book/BookingRequest/AssessmentType"; | ||
|
||
private readonly WebApplicationFactory<Program> _factory; | ||
|
||
private static readonly IEnumerable<AssessmentType> SampleAssessmentTypes = new List<AssessmentType> | ||
{ | ||
new() { Id = "assessment_type_30", DisplayNameLowerCase = "Assessment Type 30", IsEnabled = false, SortOrder = 30}, | ||
new() { Id = "assessment_type_10", DisplayNameLowerCase = "Assessment Type 10", IsEnabled = true, SortOrder = 10}, | ||
new() { Id = "assessment_type_20", DisplayNameLowerCase = "Assessment Type 20", IsEnabled = false, SortOrder = 20}, | ||
}; | ||
|
||
public QuestionAssessmentTypePostTests(WebApplicationFactory<Program> factory) | ||
{ | ||
_factory = factory; | ||
} | ||
|
||
|
||
private WebApplicationFactory<Program> FactoryWithFakeApiReturningThreeAssessmentTypes => _factory.WithWebHostBuilder(builder => | ||
{ | ||
builder.ConfigureTestServices(services => | ||
{ | ||
services.AddScoped<ILookupsReadService, FakeLookupsReadServiceThreeAssessmentTypes>(); | ||
}); | ||
}); | ||
|
||
|
||
|
||
[Fact] | ||
public async Task GivenApiDefiningAssessmentTypes_WhenSubmittingValidValues_ResponseCodeMustBeRedirect() | ||
{ | ||
// Arrange | ||
var client = FactoryWithFakeApiReturningThreeAssessmentTypes | ||
.CreateClient( | ||
new WebApplicationFactoryClientOptions | ||
{ | ||
// On form submission, should receive POST and redirect -- do not follow this redirect | ||
AllowAutoRedirect = false, | ||
} | ||
); | ||
|
||
// Must perform post after a get, to first generate/fetch/include the CSRF token (used within the subsequent post) | ||
var responseGet = await client.GetAsync(Url); | ||
var contentGet = await HtmlHelpers.GetDocumentAsync(responseGet); | ||
|
||
// Get the (single) form/submit button from within the page body (ignoring, e.g., forms within the header/footer). | ||
// If more than one is found, this is a problem and should be flagged as an error. | ||
var mainPageContentSection = contentGet.QuerySelectorAll(".main--content").Single(); | ||
var formElement = mainPageContentSection.QuerySelectorAll<IHtmlFormElement>("form").Single(); | ||
var submitButtonElement = formElement.QuerySelectorAll<IHtmlButtonElement>("button[type='submit']").Single(); | ||
|
||
// Set one of the values to "checked" (i.e., radio button is selected) | ||
var arbitraryIdFromApi = SampleAssessmentTypes.Where(x => x.IsEnabled).Take(1).Single().Id; | ||
var radioElements = formElement.QuerySelectorAll<IHtmlInputElement>("input[name='" + AssessmentTypeDto.FormName + "']"); | ||
var peerReviewRadioElement = radioElements.Single(x => arbitraryIdFromApi.Equals(x.GetAttribute("value"), StringComparison.Ordinal)); | ||
peerReviewRadioElement.IsChecked = true; | ||
|
||
|
||
// Act | ||
var postResponse = await client.SendAsync(formElement, submitButtonElement); | ||
|
||
// Assert | ||
// TODO: Consider extracting this out to helper method, to avoid duplication across tests? | ||
using (new AssertionScope()) | ||
{ | ||
postResponse.StatusCode.Should().Be(System.Net.HttpStatusCode.Redirect, "because a successful form submission should accept/process the POST then redirect to the next page (preventing the user from refreshing the browser and re-submitting data)"); | ||
|
||
// Redundant additional checks, but provides extra information in case of failure. | ||
int statusCode = (int)postResponse.StatusCode; | ||
statusCode.Should().NotBe(400, "because if this is returned, it indicates the form value was not received (likely developer/test error, a mistake in setting up the fake API responses perhaps? note that if the radio/option is disabled, the value does not get submitted with the form)"); | ||
statusCode.Should().NotBe(422, "because if this is returned, an unexpected/unprocessable form value was submitted (likely developer/test error)"); | ||
} | ||
} | ||
|
||
// TODO: Validate where the form redirects to on a successful submit | ||
// - should be the next page in the flow, e.g., the "request started" question page or the "select project phase" question page (if implemented) | ||
|
||
// TODO: Consider side-effects (trigger of a write API request) | ||
// - Current flow is that a successful submit creates a new assessment request | ||
// - Next implementations will ask about the phase (which may/may not defer creating a new assessment request) | ||
|
||
// TODO: Error cases | ||
// - No form value | ||
// - Wholly inappropriate value | ||
// - Normally valid but disabled value | ||
// - Check what error messages get returned in each of the above cases | ||
// - Missing CSRF token | ||
// - Invalid CSRF token | ||
// - Re-used CSRF token | ||
|
||
|
||
|
||
public class FakeLookupsReadServiceThreeAssessmentTypes : FakeNotImplementedLookupsReadService | ||
{ | ||
public override Task<IEnumerable<AssessmentType>> GetAssessmentTypes() | ||
{ | ||
return Task.FromResult(SampleAssessmentTypes); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.