param( [switch]$SkipDotnetRestore, [switch]$SkipBuild, [switch]$SkipPlaywright ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" function Invoke-Step { param( [Parameter(Mandatory = $true)][string]$Name, [Parameter(Mandatory = $true)][scriptblock]$Action ) Write-Host "==> $Name" & $Action if ($LASTEXITCODE -ne 0) { throw "Step failed: $Name" } } function Remove-TestCoverageArtifacts { param( [Parameter(Mandatory = $true)][string]$ResultsRoot ) if (-not (Test-Path $ResultsRoot)) { return } Get-ChildItem -Path $ResultsRoot -Recurse -File -Filter "coverage.cobertura.xml" -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue Get-ChildItem -Path $ResultsRoot -Recurse -Directory -ErrorAction SilentlyContinue | Sort-Object FullName -Descending | ForEach-Object { if (-not (Get-ChildItem -Path $_.FullName -Force -ErrorAction SilentlyContinue | Select-Object -First 1)) { Remove-Item -Path $_.FullName -Force -ErrorAction SilentlyContinue } } } $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $repoRoot = Split-Path -Parent $scriptDir $testResultsRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("RpgRoller.TestResults." + [Guid]::NewGuid().ToString("N")) $repoCoverageResultsRoot = Join-Path $repoRoot "RpgRoller.Tests\TestResults" Push-Location $repoRoot try { Remove-TestCoverageArtifacts -ResultsRoot $repoCoverageResultsRoot if (-not $SkipDotnetRestore) { Invoke-Step -Name "Restore .NET solution" -Action { dotnet restore RpgRoller.sln --verbosity minimal } } if (-not $SkipBuild) { Invoke-Step -Name "Build .NET solution (warnings as errors)" -Action { dotnet build RpgRoller.sln --no-restore -warnaserror --verbosity minimal } } Invoke-Step -Name "Restore Node dependencies" -Action { npm ci } Invoke-Step -Name "Ensure Playwright browser" -Action { npm exec playwright install chromium } Invoke-Step -Name "Run tests" -Action { if ($SkipBuild) { dotnet test RpgRoller.Tests/RpgRoller.Tests.csproj --verbosity minimal --collect:"XPlat Code Coverage" --settings RpgRoller.Tests/coverlet.runsettings --results-directory $testResultsRoot } else { dotnet test RpgRoller.Tests/RpgRoller.Tests.csproj --no-build --verbosity minimal --collect:"XPlat Code Coverage" --settings RpgRoller.Tests/coverlet.runsettings --results-directory $testResultsRoot } } Invoke-Step -Name "Enforce coverage thresholds" -Action { pwsh ./scripts/check-coverage.ps1 -MinLineRate 0.90 -MinBranchRate 0.70 -ResultsRoot $testResultsRoot } if (-not $SkipPlaywright) { Invoke-Step -Name "Run Playwright smoke test" -Action { pwsh ./scripts/run-playwright.ps1 } } Write-Host "CI checks passed." } finally { if (Test-Path $testResultsRoot) { Remove-Item -Path $testResultsRoot -Recurse -Force -ErrorAction SilentlyContinue } Remove-TestCoverageArtifacts -ResultsRoot $repoCoverageResultsRoot Pop-Location }