Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added baseline code for page object model #10

Merged
merged 8 commits into from
Jul 3, 2024
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
13 changes: 5 additions & 8 deletions Dfe.Data.SearchPrototype.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Data.SearchPrototype.We
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Data.SearchPrototype.Tests", "Dfe.Data.SearchPrototype\Tests\Dfe.Data.SearchPrototype.Tests.csproj", "{011C329A-A195-439A-ACED-F20AFF560FE7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Data.SearchPrototype.Web.Tests", "Dfe.Data.SearchPrototype\Web\Tests\Dfe.Data.SearchPrototype.Web.Tests.csproj", "{650783D2-FCBB-473B-BCBE-5B3E007540FE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Data.SearchPrototype.Web.Tests", "Dfe.Data.SearchPrototype\Web\Tests\Dfe.Data.SearchPrototype.Web.Tests.csproj", "{A6882653-52E1-472D-97F4-03C0FB8D0B2F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{0D4ACC06-9FA9-422A-8F92-92B565879E16}"
ProjectSection(SolutionItems) = preProject
NuGet\DfE.Data.ComponentLibrary.CleanArchitecture.2.0.37-beta-ci-20240610-104522.nupkg = NuGet\DfE.Data.ComponentLibrary.CleanArchitecture.2.0.37-beta-ci-20240610-104522.nupkg
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -34,10 +31,10 @@ Global
{011C329A-A195-439A-ACED-F20AFF560FE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{011C329A-A195-439A-ACED-F20AFF560FE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{011C329A-A195-439A-ACED-F20AFF560FE7}.Release|Any CPU.Build.0 = Release|Any CPU
{650783D2-FCBB-473B-BCBE-5B3E007540FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{650783D2-FCBB-473B-BCBE-5B3E007540FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{650783D2-FCBB-473B-BCBE-5B3E007540FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{650783D2-FCBB-473B-BCBE-5B3E007540FE}.Release|Any CPU.Build.0 = Release|Any CPU
{A6882653-52E1-472D-97F4-03C0FB8D0B2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A6882653-52E1-472D-97F4-03C0FB8D0B2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6882653-52E1-472D-97F4-03C0FB8D0B2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A6882653-52E1-472D-97F4-03C0FB8D0B2F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
6 changes: 6 additions & 0 deletions Dfe.Data.SearchPrototype/Dfe.Data.SearchPrototype.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@
</PropertyGroup>

<ItemGroup>
<Compile Remove="Application\**" />
<Compile Remove="Dfe.Data.SearchPrototype\**" />
<Compile Remove="Infrastructure\**" />
<Compile Remove="Tests\**" />
<Compile Remove="Web\**" />
<EmbeddedResource Remove="Application\**" />
<EmbeddedResource Remove="Dfe.Data.SearchPrototype\**" />
<EmbeddedResource Remove="Infrastructure\**" />
<EmbeddedResource Remove="Tests\**" />
<EmbeddedResource Remove="Web\**" />
<None Remove="Application\**" />
<None Remove="Dfe.Data.SearchPrototype\**" />
<None Remove="Infrastructure\**" />
<None Remove="Tests\**" />
<None Remove="Web\**" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,20 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>

<ItemGroup>
<Compile Remove="Unit\**" />
<EmbeddedResource Remove="Unit\**" />
<None Remove="Unit\**" />
</ItemGroup>

<ItemGroup>
<Folder Include="Integration\" />
</ItemGroup>


<ItemGroup>
<PackageReference Include="AngleSharp" Version="1.1.2" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.6" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="WireMock.Net" Version="1.5.58" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
<PackageReference Include="AngleSharp" Version="1.1.2" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.6" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="WireMock.Net" Version="1.5.58" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Dfe.Data.SearchPrototype.Web.csproj" />
</ItemGroup>

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

<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
43 changes: 26 additions & 17 deletions Dfe.Data.SearchPrototype/Web/Tests/Integration/HomePageTests.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1,44 @@
using AngleSharp.Dom;
using AngleSharp.Html.Dom;
using DfE.Data.SearchPrototype.Test.Shared;
using DfE.Data.SearchPrototype.Web.Tests.Integration.PageObjectModel;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;

namespace DfE.Data.SearchPrototype.Test;
namespace DfE.Data.SearchPrototype.Web.Tests.Integration;

public class HomePageTests : PageTestHelper
public class HomePageTests : IClassFixture<WebApplicationFactory<Program>>
{
public HomePageTests(WebApplicationFactory<Program> factory) : base(factory)
private readonly HomePage _homePage;
private readonly WebApplicationFactory<Program> _webApplicationFactory;

public HomePageTests(WebApplicationFactory<Program> webApplicationFactory)
{
_webApplicationFactory = webApplicationFactory;
_homePage = HomePage.Create(webApplicationFactory);
}

[Fact]
public async Task HomePage_ContainsExpectedTitle()
public void HomePage_ContainsExpectedTitle()
{
// act
var response = await NavigateToPage("");
string searchHeading = _homePage.GetHomePageHeading();

// assert
var headings = response.GetElementsByTagName("h1");
Assert.Equal("Welcome", headings.First().InnerHtml);
Assert.Equal("Search prototype", searchHeading);
}

[Fact]
public async Task HomePage_ContainsPrivacyLink()
public void HomePage_ContainsPrivacyLink()
{
// act
IDocument response = await NavigateToPage("");
IHtmlAnchorElement privacyLink = _homePage.GetHomePagePrivacyLink();

// Assert
IHtmlAnchorElement privacyLink = response.GetHeaderLink("Privacy");
Assert.Equal("/Home/Privacy", privacyLink.PathName);
}
}

[Fact]
public void HomePage_PrivacyLink_GoesToPrivacyPage()
{
IHtmlAnchorElement privacyLink = _homePage.GetHomePagePrivacyLink();

var privacyPage = PrivacyPage.NavigateToPage(_webApplicationFactory, privacyLink.Href);

Assert.Equal("Privacy Policy", privacyPage.GetPrivacyPageTitle());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using AngleSharp.Html.Dom;
using DfE.Data.SearchPrototype.Web.Tests.Integration.PageObjectModel.PageComponents;
using DfE.Data.SearchPrototype.Web.Tests.Integration.PageObjectModel.Setup;
using Microsoft.AspNetCore.Mvc.Testing;

namespace DfE.Data.SearchPrototype.Web.Tests.Integration.PageObjectModel
{
public sealed class HomePage : DocumentObjectModelExtractor
{
private readonly PageHeader _pageHeader;

private const string PageName = "Home";
private const string PrivacyLink = "Privacy";
private const string MainHeadingClass = "govuk-header__link govuk-header__service-name";

public HomePage(WebApplicationFactory<Program> webApplicationFactory) :
base(webApplicationFactory, PageName)
{
_pageHeader = PageHeader.Create(DocumentObjectModel);
}

public string GetHomePageHeading() =>
_pageHeader.GetMainHeading(MainHeadingClass);

public IHtmlAnchorElement GetHomePagePrivacyLink() =>
_pageHeader.GetHeaderLink(PrivacyLink);

public static HomePage Create(
WebApplicationFactory<Program> webApplicationFactory) => new(webApplicationFactory);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using AngleSharp.Dom;

namespace Dfe.Data.SearchPrototype.Web.Tests.Integration.PageObjectModel.PageComponents
{
public abstract class PageComponent
{
protected IElement? PageElement { get; private set; }

protected PageComponent(IDocument documentObjectModel, string tagName)
{
SetPageElement(documentObjectModel, tagName);
}

private void SetPageElement(
IDocument documentObjectModel, string tagName)
{
PageElement =
documentObjectModel.GetElementsByTagName(tagName).SingleOrDefault() ??
throw new InvalidOperationException(
$"Unable to derive element {tagName}.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using AngleSharp.Dom;
using AngleSharp.Html.Dom;
using Dfe.Data.SearchPrototype.Web.Tests.Integration.PageObjectModel.PageComponents;

namespace DfE.Data.SearchPrototype.Web.Tests.Integration.PageObjectModel.PageComponents
{
public sealed class PageHeader : PageComponent
{
private const string HeaderElementTag = "header";

private PageHeader(IDocument documentObjectModel)
: base(documentObjectModel, HeaderElementTag){
}

public string GetMainHeading(string headingClass) =>
PageElement == null ?
throw new InvalidOperationException(
"Unable to derive the main heading in page.") :
PageElement
.GetElementsByClassName(headingClass).Single().InnerHtml;

public IHtmlAnchorElement GetHeaderLink(string linkName) =>
PageElement == null ?
throw new InvalidOperationException(
$"Unable to derive the search link: {linkName} in page.") :
(IHtmlAnchorElement)PageElement
.GetElementsByTagName("a")
.Single(anchorTags => anchorTags.TextContent.Contains(linkName));

public static PageHeader Create(IDocument documentObjectModel) => new(documentObjectModel);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using DfE.Data.SearchPrototype.Web.Tests.Integration.PageObjectModel.Setup;
using Microsoft.AspNetCore.Mvc.Testing;

namespace DfE.Data.SearchPrototype.Web.Tests.Integration.PageObjectModel
{
public sealed class PrivacyPage : DocumentObjectModelExtractor
{
private const string PageName = "Privacy";
private const string TitleElement = "h1";

public PrivacyPage(WebApplicationFactory<Program> webApplicationFactory, string pageName) :
base(webApplicationFactory, pageName){
}

public string GetPrivacyPageTitle() =>
DocumentObjectModel.GetElementsByTagName(TitleElement).Single().InnerHtml;

public static PrivacyPage NavigateToPage(
WebApplicationFactory<Program> webApplicationFactory, string pageName) => new(webApplicationFactory, pageName);

public static PrivacyPage Create(
WebApplicationFactory<Program> webApplicationFactory) => new(webApplicationFactory, PageName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using AngleSharp;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Mvc.Testing;

namespace DfE.Data.SearchPrototype.Web.Tests.Integration.PageObjectModel.Setup;

public abstract class DocumentObjectModelExtractor : WebApplicationBootstrapper
{
protected IDocument DocumentObjectModel { get; private set; }

protected DocumentObjectModelExtractor(

Check warning on line 11 in Dfe.Data.SearchPrototype/Web/Tests/Integration/PageObjectModel/Setup/DocumentObjectModelExtractor.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'DocumentObjectModel' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 11 in Dfe.Data.SearchPrototype/Web/Tests/Integration/PageObjectModel/Setup/DocumentObjectModelExtractor.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'DocumentObjectModel' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
WebApplicationFactory<Program> webApplicationFactory, string? pageName)
: base(webApplicationFactory)
{
if (string.IsNullOrWhiteSpace(pageName)){
throw new ArgumentNullException(nameof(pageName));
}

SetPageObject(pageName);
}

protected void SetPageObject(string pageName)
{
Task.Run(async () => {
HttpResponseMessage response = await HttpClient.GetAsync(pageName);
string documentObjectModel = await response.Content.ReadAsStringAsync();

DocumentObjectModel = await BrowsingContext
.New(Configuration.Default)
.OpenAsync(response => response.Content(documentObjectModel));
})
.Wait();

if (DocumentObjectModel == null){
throw new InvalidOperationException(
$"Unable to derive document object model for page {pageName}.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;

namespace DfE.Data.SearchPrototype.Web.Tests.Integration.PageObjectModel.Setup;

public abstract class WebApplicationBootstrapper : IClassFixture<WebApplicationFactory<Program>>
{
protected HttpClient HttpClient { get; }

/// <summary>
/// WebApplicationFactory<Program> is injected by xUnit, which creates and calls Dispose.
/// </summary>
protected WebApplicationBootstrapper(WebApplicationFactory<Program> fixture)
{
HttpClient = fixture.CreateClient();
}
}

This file was deleted.

This file was deleted.

Loading