feat: add site header (#2)

* tests: setup initial infra for end to end tests

* tests: rework end to end host

* tests: remove need for bang operator

* tests: correct header component tests

* docs: add README.md to blog directory with instructions on setting up local dev environment with and without docker

* docs: update non-developer run command

* feat: finish layout of header

* feat: add favicon

* tests: setup test coverage

* chore: update workflow with missing playwright step

* chore: add install command
This commit is contained in:
Stevan Freeborn
2024-04-08 23:34:32 -05:00
committed by GitHub
parent 5ccb9848ed
commit d25e5d4944
26 changed files with 387 additions and 21 deletions
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<Playwright>
<BrowserName>chromium</BrowserName>
<LaunchOptions>
<Headless>false</Headless>
<Channel>msedge</Channel>
</LaunchOptions>
</Playwright>
</RunSettings>
+23 -1
View File
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
@@ -11,6 +11,12 @@
<ItemGroup>
<PackageReference Include="bunit" Version="1.27.17" />
<PackageReference Include="coverlet.msbuild" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="Microsoft.Playwright.NUnit" Version="1.42.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
@@ -19,6 +25,22 @@
<PackageReference Include="coverlet.collector" Version="6.0.0" />
</ItemGroup>
<PropertyGroup>
<CollectCoverage>true</CollectCoverage>
<CoverletOutput>./TestResults/coverage/</CoverletOutput>
<CoverletOutputFormat>cobertura</CoverletOutputFormat>
<Include>[Blog]*</Include>
<ExcludeByFile>**/Blog/Program.cs</ExcludeByFile>
</PropertyGroup>
<Target Name="GenerateHtmlCoverageReport" AfterTargets="GenerateCoverageResultAfterTest">
<Exec Command="reportgenerator -reports:./TestResults/coverage/*.xml -targetdir:./TestResults/coverage/report/ -reporttypes:Html_Dark" />
</Target>
<Target Name="OpenTestReport" AfterTargets="GenerateHtmlCoverageReport" Condition="'$(DOTNET_ENVIRONMENT)' != 'CI'">
<Exec Command="start ./TestResults/coverage/report/index.html" />
</Target>
<ItemGroup>
<ProjectReference Include="..\..\src\Blog\Blog.csproj" />
</ItemGroup>
@@ -0,0 +1,62 @@
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
namespace Blog.Tests.EndToEnd;
public class BlogHostFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class
{
private IHost? _host;
private void EnsureServer()
{
if (_host is null)
{
using var _ = CreateDefaultClient();
}
}
protected override void Dispose(bool disposing)
{
_host?.Dispose();
}
public string ServerAddress
{
get
{
EnsureServer();
return ClientOptions.BaseAddress.ToString();
}
}
protected override IHost CreateHost(IHostBuilder builder)
{
var testHost = builder
.ConfigureWebHost(
webHostBuilder => webHostBuilder.ConfigureLogging(
config => config.ClearProviders()
)
)
.Build();
builder.ConfigureWebHost(
webHostBuilder => webHostBuilder.UseKestrel(
o => o.Listen(IPAddress.Loopback, 0)
)
);
_host = builder.Build();
_host.Start();
var server = _host.Services.GetRequiredService<IServer>();
var addresses = server.Features.GetRequiredFeature<IServerAddressesFeature>();
ClientOptions.BaseAddress = addresses.Addresses
.Select(x => new Uri(x))
.Last();
testHost.Start();
return testHost;
}
}
+17
View File
@@ -0,0 +1,17 @@
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
namespace Blog.Tests.EndToEnd;
[TestFixture]
public class BlogTest : PageTest
{
private readonly BlogHostFactory<Program> _factory = new();
public override BrowserNewContextOptions ContextOptions()
{
var options = base.ContextOptions();
options.BaseURL = _factory.ServerAddress;
return options;
}
}
+32
View File
@@ -0,0 +1,32 @@
namespace Blog.Tests.EndToEnd;
[TestFixture]
public class HomeTests : BlogTest
{
[Test]
public async Task Home_WhenNavigatedTo_ItShouldDisplayCorrectPageTitle()
{
await Page.GotoAsync("/");
var title = await Page.TitleAsync();
title.Should().Be("journal - A blog by Stevan Freeborn");
}
[Test]
public async Task Home_WhenNavigatedTo_ItShouldCorrectSiteTitle()
{
await Page.GotoAsync("/");
var message = Page.GetByRole(AriaRole.Heading, new () { Name = "journal" });
await Expect(message).ToBeVisibleAsync();
}
[Test]
public async Task Home_WhenNavigatedTo_ItShouldDisplayWrittenByAttribution()
{
await Page.GotoAsync("/");
var message = Page.GetByText("by");
var image = Page.GetByAltText("Stevan Freeborn");
await Expect(message).ToBeVisibleAsync();
await Expect(image).ToBeVisibleAsync();
}
}
+10 -1
View File
@@ -1 +1,10 @@
global using NUnit.Framework;
global using NUnit.Framework;
global using Microsoft.AspNetCore.Mvc.Testing;
global using Microsoft.Extensions.Hosting;
global using Microsoft.AspNetCore.Hosting;
global using Microsoft.Playwright.NUnit;
global using Microsoft.Playwright;
global using FluentAssertions;
global using Microsoft.AspNetCore.Hosting.Server;
global using Microsoft.Extensions.DependencyInjection;
global using System.Net;
+32
View File
@@ -0,0 +1,32 @@
@inherits Bunit.TestContext
@code
{
[Test]
public void Header_WhenRendered_ItShouldContainSiteTitle()
{
var cut = Render(@<Header />);
var heading = cut.Find("h1");
heading.MarkupMatches(@<h1>journal</h1>);
}
[Test]
public void Header_WhenRendered_ItShouldContainAuthorInfo()
{
var cut = Render(@<Header />);
var author = cut.Find(".author-container > span");
author.MarkupMatches(@<span>by</span>);
}
[Test]
public void Header_WhenRendered_ItShouldContainAuthorImage()
{
var cut = Render(@<Header />);
var image = cut.Find("img[alt='Stevan Freeborn']");
image.Should().NotBeNull();
}
}
-15
View File
@@ -1,15 +0,0 @@
namespace Blog.Tests;
public class Tests
{
[SetUp]
public void Setup()
{
}
[Test]
public void Test1()
{
Assert.Pass();
}
}
+3
View File
@@ -0,0 +1,3 @@
@using Bunit
@using Blog.Components
@using FluentAssertions