diff --git a/EstateManagementUI.BlazorServer.Tests.Integration/EstateManagementUI.BlazorServer.Tests.Integration.csproj b/EstateManagementUI.BlazorServer.Tests.Integration/EstateManagementUI.BlazorServer.Tests.Integration.csproj new file mode 100644 index 00000000..12f83fe9 --- /dev/null +++ b/EstateManagementUI.BlazorServer.Tests.Integration/EstateManagementUI.BlazorServer.Tests.Integration.csproj @@ -0,0 +1,28 @@ + + + + net10.0 + enable + enable + false + true + + + + + + + + + + + + + + + + + + + + diff --git a/EstateManagementUI.BlazorServer.Tests.Integration/Properties/AssemblyInfo.cs b/EstateManagementUI.BlazorServer.Tests.Integration/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..20a09636 --- /dev/null +++ b/EstateManagementUI.BlazorServer.Tests.Integration/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +using NUnit.Framework; + +// Configure NUnit for parallel execution at the Children level +// This allows scenarios to run in parallel, which is ideal for browser-based tests +[assembly: Parallelizable(ParallelScope.Children)] +[assembly: LevelOfParallelism(4)] diff --git a/EstateManagementUI.BlazorServer.Tests.Integration/README.md b/EstateManagementUI.BlazorServer.Tests.Integration/README.md new file mode 100644 index 00000000..fb7d43f5 --- /dev/null +++ b/EstateManagementUI.BlazorServer.Tests.Integration/README.md @@ -0,0 +1,196 @@ +# EstateManagementUI.BlazorServer.Tests.Integration + +**Testcontainers-based integration test project** for the Blazor Server UI using Playwright for browser automation and Reqnroll for BDD-style scenarios. + +## Overview + +This project provides a "zero-process" testing environment where the Blazor application runs in a Docker container and is tested using BDD-style scenarios. + +### Key Features + +1. **Pre-built Image Testing**: Uses a pre-built Docker image (`estatemanagementui-blazor`) for fast test execution +2. **Testcontainers Integration**: Uses official Testcontainers for .NET to manage full container lifecycle +3. **Browser Automation**: Microsoft Playwright for cross-browser testing (Chromium, Firefox, WebKit) +4. **BDD Framework**: Reqnroll (modern SpecFlow successor) with NUnit test runner +5. **Parallel Execution**: Tests run in parallel at Children level (4 concurrent scenarios) +6. **Test Environment**: Application runs in "Test" mode with isolated data (no external API calls) + +## Prerequisites + +Before running tests, build the Docker image locally: + +```bash +cd EstateManagementUI.BlazorServer +docker build -t estatemanagementui-blazor . +``` + +## Technical Architecture + +### Container Management + +The `DockerHelper` class: +- Uses pre-built Docker image `estatemanagementui-blazor` +- Starts container with Test environment configuration +- Manages lifecycle from setup to teardown +- Implements `IAsyncDisposable` for proper resource cleanup + +```csharp +// Uses pre-built image +_blazorServerImageName = "estatemanagementui-blazor"; + +var container = new ContainerBuilder() + .WithImage(_blazorServerImageName) + .WithEnvironment("ASPNETCORE_ENVIRONMENT", "Test") + .WithEnvironment("AppSettings:TestMode", "true") + .Build(); +``` + +### Test Lifecycle (Hooks) + +Reqnroll hooks manage the complete test lifecycle: + +- **BeforeTestRun (Order 0)**: Start Docker containers from pre-built image +- **BeforeTestRun (Order 100)**: Initialize Playwright +- **BeforeScenario**: Create browser page, register DockerHelper +- **AfterScenario**: Take screenshots on failure, close page +- **AfterTestRun (Order 0)**: Close Playwright +- **AfterTestRun (Order 100)**: Stop containers and cleanup + +### Parallel Execution + +Configured in `Properties/AssemblyInfo.cs`: + +```csharp +[assembly: Parallelizable(ParallelScope.Children)] +[assembly: LevelOfParallelism(4)] +``` + +## Project Structure + +``` +EstateManagementUI.BlazorServer.Tests.Integration/ +├── Support/ +│ ├── DockerHelper.cs # Testcontainers container management +│ └── Hooks.cs # Reqnroll lifecycle hooks +├── Features/ +│ └── Framework/ +│ ├── FrameworkCheck.feature # Smoke test +│ └── FrameworkCheckSteps.cs # Step definitions +├── Properties/ +│ └── AssemblyInfo.cs # NUnit parallel configuration +└── EstateManagementUI.BlazorServer.Tests.Integration.csproj +``` + +## Running Tests + +### Prerequisites + +1. **.NET 10.0 SDK** or later +2. **Docker** (Docker Desktop on Windows/Mac, Docker Engine on Linux) +3. **Pre-built Docker image**: Build the image before running tests: + ```bash + cd EstateManagementUI.BlazorServer + docker build -t estatemanagementui-blazor . + ``` + +### Quick Start + +```bash +# Ensure the Docker image is built first +docker images | grep estatemanagementui-blazor + +# Navigate to the test project +cd EstateManagementUI.BlazorServer.Tests.Integration + +# Run all tests (uses pre-built image) +dotnet test + +# Run only framework smoke tests +dotnet test --filter "Category=framework" + +# Run with specific browser +Browser=Firefox dotnet test + +# Run in headless mode (CI) +IsCI=true dotnet test +``` + +### Test Execution Flow + +1. **Image Check**: Tests use pre-built `estatemanagementui-blazor` image +2. **Container Start**: Container started with test-specific configuration +3. **Browser Setup**: Playwright initializes configured browser(s) +4. **Test Run**: Tests execute in parallel (up to 4 scenarios) +5. **Cleanup**: Containers stopped, browsers closed, Docker resources cleaned up + +### Environment Variables + +- **`Browser`**: Browser type (`Chrome`, `Firefox`, `WebKit`) - defaults to Chrome +- **`IsCI`**: Set to `true` for headless mode - defaults to false (headed mode) + +## Framework Check Feature + +The `FrameworkCheck.feature` validates the infrastructure: + +```gherkin +@framework @smoke +Feature: Framework Check +Background: + Given the application is running in a container + +Scenario: Home page is accessible + When I navigate to the home page + Then the page title should be visible +``` + +This test validates: +- ✓ Pre-built Docker image exists and can be used +- ✓ Container starts with proper configuration +- ✓ Playwright connects to containerized application +- ✓ Basic navigation works + +## Package Dependencies + +- **Testcontainers** 4.2.0 - Official Testcontainers for .NET +- **Microsoft.Playwright** 1.49.0 - Browser automation +- **Reqnroll** 3.2.1 - BDD framework (SpecFlow successor) +- **NUnit** 4.4.0 - Test runner +- **Shouldly** 4.3.0 - Assertion library + +## Troubleshooting + +### Image Not Found + +1. Build the Docker image first: + ```bash + cd EstateManagementUI.BlazorServer + docker build -t estatemanagementui-blazor . + ``` +2. Verify image exists: `docker images | grep estatemanagementui-blazor` + +### Container Won't Start + +1. Check Docker is running: `docker ps` +2. Check Docker logs: `docker logs ` +3. Verify port 5004 not already in use +4. Check Docker has sufficient resources + +### Playwright Issues + +1. Browsers installed automatically in BeforeTestRun +2. Check container accessible: `curl -k https://localhost:` +3. Verify SSL certificate handling configured + +## Future Expansion + +The project structure supports adding more feature files: + +``` +Features/ +├── Framework/ # Infrastructure tests +├── Authentication/ # Auth flow tests +├── Estates/ # Estate management tests +└── Reporting/ # Reporting feature tests +``` + +Each feature area can have its own `.feature` files and step definitions. diff --git a/EstateManagementUI.sln b/EstateManagementUI.sln index dd971587..ca45dc0d 100644 --- a/EstateManagementUI.sln +++ b/EstateManagementUI.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EstateManagementUI.BlazorSe EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EstateManagementUI.BlazorServer.Tests", "EstateManagementUI.BlazorServer.Tests\EstateManagementUI.BlazorServer.Tests.csproj", "{55431CBE-C879-47B9-9607-A5822DE9B856}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EstateManagementUI.BlazorServer.Tests.Integration", "EstateManagementUI.BlazorServer.Tests.Integration\EstateManagementUI.BlazorServer.Tests.Integration.csproj", "{A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,18 @@ Global {55431CBE-C879-47B9-9607-A5822DE9B856}.Release|x64.Build.0 = Release|Any CPU {55431CBE-C879-47B9-9607-A5822DE9B856}.Release|x86.ActiveCfg = Release|Any CPU {55431CBE-C879-47B9-9607-A5822DE9B856}.Release|x86.Build.0 = Release|Any CPU + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Debug|x64.ActiveCfg = Debug|Any CPU + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Debug|x64.Build.0 = Debug|Any CPU + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Debug|x86.ActiveCfg = Debug|Any CPU + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Debug|x86.Build.0 = Debug|Any CPU + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Release|Any CPU.Build.0 = Release|Any CPU + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Release|x64.ActiveCfg = Release|Any CPU + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Release|x64.Build.0 = Release|Any CPU + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Release|x86.ActiveCfg = Release|Any CPU + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -52,6 +66,7 @@ Global GlobalSection(NestedProjects) = preSolution {C52FE3F3-E999-4A12-A20B-0D5C6DA34AA9} = {05420FAB-1C50-4AAD-9E7C-2A86184019B7} {55431CBE-C879-47B9-9607-A5822DE9B856} = {E7671C23-F30C-471A-A9EF-AC85DC607B55} + {A7D3E9F1-8B2C-4D5E-9F6A-1C3E5B7D9A2F} = {E7671C23-F30C-471A-A9EF-AC85DC607B55} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C5AE6DA4-B167-44E5-9B63-61C7E8D1BC9F}