Skip to content

Commit

Permalink
Merge pull request #151 from DFE-Digital/Development
Browse files Browse the repository at this point in the history
v0.8.0 release
  • Loading branch information
jimwashbrook authored Aug 28, 2024
2 parents a4ecec2 + bb501c7 commit a350cbe
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/code-pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
/d:sonar.login="${{ secrets.SONAR_TOKEN }}" \
/d:sonar.host.url="https://sonarcloud.io" \
/d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml \
/d:sonar.coverage.exclusions=**/Program.cs,**/wwwroot/** \
/d:sonar.coverage.exclusions=**/Program.cs,**/wwwroot/**,**/Dfe.ContentSupport.Web.E2ETests/** \
/d:sonar.issue.ignore.multicriteria=e1 \
/d:sonar.issue.ignore.multicriteria.e1.ruleKey=csharpsquid:S6602 \
/d:sonar.issue.ignore.multicriteria.e1.resourceKey=src/**/*.cs
Expand Down
39 changes: 31 additions & 8 deletions src/Dfe.ContentSupport.Web/Controllers/ContentController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ namespace Dfe.ContentSupport.Web.Controllers;

[Route("/content")]
[AllowAnonymous]
public class ContentController(IContentService contentService, ILayoutService layoutService)
public class ContentController(IContentService contentService, ILayoutService layoutService, ILogger<ContentController> logger)
: Controller
{
public const string ErrorActionName = "error";

public async Task<IActionResult> Home()
{
var defaultModel = new CsPage
Expand All @@ -33,16 +35,37 @@ public async Task<IActionResult> Home()
[HttpGet("{slug}/{page?}")]
public async Task<IActionResult> Index(string slug, string page = "", bool isPreview = false, [FromQuery] List<string>? tags = null)
{
if (!ModelState.IsValid) return RedirectToAction("error");
if (string.IsNullOrEmpty(slug)) return RedirectToAction("error");
if (!ModelState.IsValid)
{
logger.LogError("Invalid model state received for {Controller} {Action} with slug {Slug}", nameof(ContentController), nameof(Index), slug);
return RedirectToAction(ErrorActionName);
}

if (string.IsNullOrEmpty(slug))
{
logger.LogError("No slug received for C&S {Controller} {Action}", nameof(ContentController), nameof(Index));
return RedirectToAction(ErrorActionName);
}

var resp = await contentService.GetContent(slug, isPreview);
if (resp is null) return RedirectToAction("error");
try
{
var resp = await contentService.GetContent(slug, isPreview);
if (resp is null)
{
logger.LogError("Failed to load content for C&S page {Slug}; no content received.", slug);
return RedirectToAction(ErrorActionName);
}

resp = layoutService.GenerateLayout(resp, Request, page);
ViewBag.tags = tags;
resp = layoutService.GenerateLayout(resp, Request, page);
ViewBag.tags = tags;

return View("CsIndex", resp);
return View("CsIndex", resp);
}
catch (Exception ex)
{
logger.LogError(ex, "Error loading C&S content page {Slug}", slug);
return RedirectToAction(ErrorActionName);
}
}


Expand Down
51 changes: 50 additions & 1 deletion src/Dfe.ContentSupport.Web/Views/Content/CsIndex.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,53 @@
<partial name="_Feedback" />
}
</div>
</div>
</div>

@section BodyEnd{
@if (Model.HasPrint)
{
<script nonce="@Context.Items["nonce"]" defer>
/**
* Adds functionality for printing a page to the Print Page button (_Print.cshtml)
*/
const printPage = () => window.print();
const addPrintButtonEventListener = () => {
const printButton = document.getElementById("print-link");
if(!printButton){
return;
}
printButton.addEventListener('click', printPage);
}
addPrintButtonEventListener();
</script>
}

<script nonce="@Context.Items["nonce"]" defer>
const hiddenClass = "govuk-visually-hidden";
//Some elements should remain hidden
const idsToNotShow = ["support-links"];
//Remove hidden visibility CSS class if JS enabled
const removeHiddenClassFromElements = () => {
const hiddenElements = Array.from(document.querySelectorAll(`.${hiddenClass}`));
for(const element of hiddenElements){
if(idsToNotShow.indexOf(element.id) > -1){
continue;
}
element.classList.remove(hiddenClass);
//Also unset "aria-hidden" attribute
element.ariaHidden = undefined;
}
}
removeHiddenClassFromElements();
</script>
}
2 changes: 1 addition & 1 deletion src/Dfe.ContentSupport.Web/Views/Shared/_Feedback.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

@if (track)
{
<div class="dfe-feedback-banner govuk-!-margin-top-9">
<div class="dfe-feedback-banner govuk-!-margin-top-9 govuk-visually-hidden" aria-hidden="true" id="feedback-banner">
<div class="dfe-feedback-banner--content">
<form id="feedbackForm">
<div class="dfe-feedback-banner--content-questions">
Expand Down
4 changes: 2 additions & 2 deletions src/Dfe.ContentSupport.Web/Views/Shared/_Footer.cshtml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<footer class="govuk-footer " role="contentinfo">
<div class="govuk-width-container ">
<div class="govuk-width-container">
<div class="govuk-footer__meta">
<div class="govuk-footer__meta-item govuk-footer__meta-item--grow">
<h2 class="govuk-visually-hidden">Support links</h2>
<h2 class="govuk-visually-hidden" id="support-links">Support links</h2>
@* @await Component.InvokeAsync("FooterLinks") *@
<svg aria-hidden="true" focusable="false" class="govuk-footer__licence-logo"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 483.2 195.7" height="17" width="41">
Expand Down
4 changes: 2 additions & 2 deletions src/Dfe.ContentSupport.Web/Views/Shared/_Print.cshtml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<div class="print-button">
<button class="govuk-link print-link-button" data-module="print-link" onclick="window.print()">Print this page</button>
<div class="print-button govuk-visually-hidden" aria-hidden="true">
<button class="govuk-link print-link-button" data-module="print-link" id="print-link" >Print this page</button>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
describe('Feedback banner', () => {
it("should be visible when tracking consented", () => {
cy.visit('content/hello-world',
{
headers: {
'Cookie': '.AspNet.Consent=yes'
}
}
);

cy.get("div#feedback-banner")
.should('exist')
.not("govuk-visually-hidden")
.should('not.have.attr', 'aria-hidden');
});

it("should not exist when tracking consent not given", () => {
cy.visit('content/hello-world',
{
headers: {
'Cookie': '.AspNet.Consent=no'
}
}
);

cy.get("div#feedback-banner")
.should('not.exist');
});

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
describe('Print button', () => {
beforeEach(() => {
cy.visit('content/hello-world', {
onBeforeLoad(win) {
//Stub the print functionality so we can see if it was called
//Note: could spy instead, but I don't want the print dialog to actually show.
cy.stub(win, 'print', () => { });
},
});
});

it("should be visible", () => {
cy.get("div.print-button")
.should('exist')
.not("govuk-visually-hidden")
.should('not.have.attr', 'aria-hidden');

cy.get("button#print-link").should("exist");
});

it("should print on click", () => {
cy.get("button#print-link").click();
cy.window().its("print").should('be.called');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
using Dfe.ContentSupport.Web.Models.Mapped;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace Dfe.ContentSupport.Web.Tests.Controllers;

public class ContentControllerTests
{
private readonly Mock<ILogger<ContentController>> _loggerMock = new();
private readonly Mock<IContentService> _contentServiceMock = new();

private ContentController GetController()
{
return new ContentController(_contentServiceMock.Object, new LayoutService());
return new ContentController(_contentServiceMock.Object, new LayoutService(), _loggerMock.Object);
}


Expand All @@ -36,6 +38,15 @@ public async Task Index_NoSlug_Returns_ErrorAction()

result.Should().BeOfType<RedirectToActionResult>();
(result as RedirectToActionResult)!.ActionName.Should().BeEquivalentTo("error");

_loggerMock.Verify(
x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((o, t) => o.ToString() != null && o.ToString()!.StartsWith($"No slug received for C&S {nameof(ContentController)} {nameof(sut.Index)}")),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}

[Fact]
Expand All @@ -53,15 +64,47 @@ public async Task Index_Calls_Service_GetContent()
[Fact]
public async Task Index_NullResponse_ReturnsErrorAction()
{
var slug = "slug";
_contentServiceMock.Setup(o => o.GetContent(It.IsAny<string>(), It.IsAny<bool>()))
.ReturnsAsync((CsPage?)null);

var sut = GetController();

var result = await sut.Index("slug");
var result = await sut.Index(slug);

result.Should().BeOfType<RedirectToActionResult>();
(result as RedirectToActionResult)!.ActionName.Should().BeEquivalentTo("error");

_loggerMock.Verify(x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((o, t) => o.ToString() != null && o.ToString()!.Equals($"Failed to load content for C&S page {slug}; no content received.")),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}

[Fact]
public async Task Index_ExceptionThrown_ReturnsErrorAction()
{
var slug = "slug";
_contentServiceMock.Setup(o => o.GetContent(It.IsAny<string>(), It.IsAny<bool>()))
.ThrowsAsync(new Exception("An exception occurred loading content"));

var sut = GetController();

var result = await sut.Index(slug);

result.Should().BeOfType<RedirectToActionResult>();
(result as RedirectToActionResult)!.ActionName.Should().BeEquivalentTo("error");

_loggerMock.Verify(x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((o, t) => o.ToString() != null && o.ToString()!.Equals($"Error loading C&S content page {slug}")),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,21 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
<PackageReference Include="FluentAssertions" Version="7.0.0-alpha.3"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageReference Include="Moq" Version="4.20.70"/>
<PackageReference Include="xunit" Version="2.5.3"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"/>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="FluentAssertions" Version="7.0.0-alpha.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit"/>
<Using Include="Xunit" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Dfe.ContentSupport.Web\Dfe.ContentSupport.Web.csproj"/>
<ProjectReference Include="..\..\src\Dfe.ContentSupport.Web\Dfe.ContentSupport.Web.csproj" />
</ItemGroup>

</Project>

0 comments on commit a350cbe

Please sign in to comment.