tests(apphost.tests): added support for end to end testing using playwright

This commit is contained in:
Stevan Freeborn
2026-02-23 05:56:34 -06:00
parent fd2cd68d73
commit d5b6fb3a38
11 changed files with 166 additions and 3 deletions
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>FiscalOS.AppHost.Tests</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.Testing" />
<PackageReference Include="AwesomeAssertions" />
<PackageReference Include="Microsoft.Playwright.Xunit.v3" />
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" />
<PackageReference Include="Moq" />
<PackageReference Include="xunit.v3.mtp-v2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\FiscalOS.AppHost\FiscalOS.AppHost.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,30 @@
using Aspire.Hosting;
using Aspire.Hosting.Testing;
namespace FiscalOS.AppHost.Tests.Infra;
public sealed class AspireFixture : IAsyncLifetime
{
private DistributedApplication _app = null!;
public async ValueTask InitializeAsync()
{
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.FiscalOS_AppHost>();
_app = await appHost.BuildAsync();
await _app.StartAsync();
}
public async ValueTask DisposeAsync()
{
await _app.StopAsync();
await _app.DisposeAsync();
}
public Uri GetBaseWebUri()
{
return _app.GetEndpoint(ProjectNames.Web, "https");
}
}
@@ -0,0 +1,48 @@
namespace FiscalOS.AppHost.Tests.Infra;
public abstract class BaseTest(
AspireFixture aspire,
PlaywrightFixture playwright
) : IAsyncLifetime, IClassFixture<AspireFixture>, IClassFixture<PlaywrightFixture>
{
protected AspireFixture AspireFixture { get; } = aspire;
protected PlaywrightFixture PlaywrightFixture { get; } = playwright;
protected IBrowserContext Context { get; private set; } = null!;
protected IPage Page { get; private set; } = null!;
public async ValueTask InitializeAsync()
{
Context = await PlaywrightFixture.Browser.NewContextAsync(new()
{
BaseURL = AspireFixture.GetBaseWebUri().ToString(),
});
Page = await Context.NewPageAsync();
}
public async ValueTask DisposeAsync()
{
await Page.CloseAsync();
await Context.CloseAsync();
await Context.DisposeAsync();
GC.SuppressFinalize(this);
}
public static ILocatorAssertions Expect(ILocator locator)
{
return Assertions.Expect(locator);
}
public static IPageAssertions Expect(IPage page)
{
return Assertions.Expect(page);
}
public static IAPIResponseAssertions Expect(IAPIResponse response)
{
return Assertions.Expect(response);
}
}
@@ -0,0 +1,26 @@
namespace FiscalOS.AppHost.Tests.Infra;
public sealed class PlaywrightFixture : IAsyncLifetime
{
private IPlaywright? _playwright;
internal IBrowser Browser { get; set; } = null!;
public async ValueTask InitializeAsync()
{
_playwright = await Playwright.CreateAsync();
Browser = await _playwright.Chromium.LaunchAsync(new()
{
Headless = false,
});
}
public async ValueTask DisposeAsync()
{
await Browser.CloseAsync();
await Browser.DisposeAsync();
_playwright?.Dispose();
}
}
@@ -0,0 +1,14 @@
namespace FiscalOS.AppHost.Tests;
public class LoginPageTests(
AspireFixture aspire,
PlaywrightFixture playwright
) : BaseTest(aspire, playwright)
{
[Fact]
public async Task LoginPage_WhenNavigatedTo_ItShouldDisplaysLoginButton()
{
await Page.GotoAsync("/public/login");
await Expect(Page.GetByText("Login")).ToBeVisibleAsync();
}
}
+4
View File
@@ -0,0 +1,4 @@
global using FiscalOS.AppHost.Tests.Infra;
global using Microsoft.Playwright;
global using Microsoft.Playwright.Xunit.v3;
@@ -0,0 +1,3 @@
{
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json"
}