diff --git a/FiscalOS.slnx b/FiscalOS.slnx index 29a95f0..a34ee68 100644 --- a/FiscalOS.slnx +++ b/FiscalOS.slnx @@ -12,6 +12,7 @@ + diff --git a/src/FiscalOS.AppHost/AppHost.cs b/src/FiscalOS.AppHost/AppHost.cs index c2665c9..6c5f73f 100644 --- a/src/FiscalOS.AppHost/AppHost.cs +++ b/src/FiscalOS.AppHost/AppHost.cs @@ -1,10 +1,12 @@ +using FiscalOS.AppHost; + var builder = DistributedApplication.CreateBuilder(args); -var api = builder.AddProject("API") +var api = builder.AddProject(ProjectNames.API) .WithHttpHealthCheck("/health"); -builder.AddViteApp("web", "../FiscalOS.Web") +builder.AddViteApp(ProjectNames.Web, "../FiscalOS.Web") .WithReference(api) .WithHttpsEndpoint(port: 7061, env: "PORT"); -builder.Build().Run(); \ No newline at end of file +builder.Build().Run(); diff --git a/src/FiscalOS.AppHost/ProjectNames.cs b/src/FiscalOS.AppHost/ProjectNames.cs new file mode 100644 index 0000000..dc60308 --- /dev/null +++ b/src/FiscalOS.AppHost/ProjectNames.cs @@ -0,0 +1,7 @@ +namespace FiscalOS.AppHost; + +public static class ProjectNames +{ + public const string API = "api"; + public const string Web = "web"; +} \ No newline at end of file diff --git a/tests/Directory.Packages.props b/tests/Directory.Packages.props index 8146a0f..0604d02 100644 --- a/tests/Directory.Packages.props +++ b/tests/Directory.Packages.props @@ -3,9 +3,12 @@ true + + + diff --git a/tests/FiscalOS.AppHost.Tests/FiscalOS.AppHost.Tests.csproj b/tests/FiscalOS.AppHost.Tests/FiscalOS.AppHost.Tests.csproj new file mode 100644 index 0000000..8a25485 --- /dev/null +++ b/tests/FiscalOS.AppHost.Tests/FiscalOS.AppHost.Tests.csproj @@ -0,0 +1,25 @@ + + + + Exe + FiscalOS.AppHost.Tests + + + + + + + + + + + + + + + + + + + + diff --git a/tests/FiscalOS.AppHost.Tests/Infra/AspireFixture.cs b/tests/FiscalOS.AppHost.Tests/Infra/AspireFixture.cs new file mode 100644 index 0000000..723d40d --- /dev/null +++ b/tests/FiscalOS.AppHost.Tests/Infra/AspireFixture.cs @@ -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(); + + _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"); + } +} \ No newline at end of file diff --git a/tests/FiscalOS.AppHost.Tests/Infra/BaseTest.cs b/tests/FiscalOS.AppHost.Tests/Infra/BaseTest.cs new file mode 100644 index 0000000..5abcaa4 --- /dev/null +++ b/tests/FiscalOS.AppHost.Tests/Infra/BaseTest.cs @@ -0,0 +1,48 @@ + + +namespace FiscalOS.AppHost.Tests.Infra; + +public abstract class BaseTest( + AspireFixture aspire, + PlaywrightFixture playwright +) : IAsyncLifetime, IClassFixture, IClassFixture +{ + 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); + } +} \ No newline at end of file diff --git a/tests/FiscalOS.AppHost.Tests/Infra/PlaywrightFixture.cs b/tests/FiscalOS.AppHost.Tests/Infra/PlaywrightFixture.cs new file mode 100644 index 0000000..598a300 --- /dev/null +++ b/tests/FiscalOS.AppHost.Tests/Infra/PlaywrightFixture.cs @@ -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(); + } +} \ No newline at end of file diff --git a/tests/FiscalOS.AppHost.Tests/LoginPageTests.cs b/tests/FiscalOS.AppHost.Tests/LoginPageTests.cs new file mode 100644 index 0000000..56566da --- /dev/null +++ b/tests/FiscalOS.AppHost.Tests/LoginPageTests.cs @@ -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(); + } +} \ No newline at end of file diff --git a/tests/FiscalOS.AppHost.Tests/Usings.cs b/tests/FiscalOS.AppHost.Tests/Usings.cs new file mode 100644 index 0000000..80c8ec2 --- /dev/null +++ b/tests/FiscalOS.AppHost.Tests/Usings.cs @@ -0,0 +1,4 @@ +global using FiscalOS.AppHost.Tests.Infra; + +global using Microsoft.Playwright; +global using Microsoft.Playwright.Xunit.v3; \ No newline at end of file diff --git a/tests/FiscalOS.AppHost.Tests/xunit.runner.json b/tests/FiscalOS.AppHost.Tests/xunit.runner.json new file mode 100644 index 0000000..86c7ea0 --- /dev/null +++ b/tests/FiscalOS.AppHost.Tests/xunit.runner.json @@ -0,0 +1,3 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json" +}