mirror of
https://github.com/MAKS-IT-COM/maksit-webui.git
synced 2026-08-15 18:08:09 +02:00
683 lines
24 KiB
PowerShell
683 lines
24 KiB
PowerShell
#requires -Version 7.0
|
|
#requires -PSEdition Core
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
PowerShell module for running tests with code coverage.
|
|
|
|
.DESCRIPTION
|
|
Provides the Invoke-TestsWithCoverage function for running .NET tests
|
|
with Coverlet code coverage collection and parsing results.
|
|
|
|
.NOTES
|
|
Author: MaksIT
|
|
Usage: pwsh -Command "Import-Module .\TestRunner.psm1"
|
|
#>
|
|
|
|
function Import-ExternalCommandSupportInternal {
|
|
if (Get-Command Invoke-ExternalCommand -ErrorAction SilentlyContinue) {
|
|
return
|
|
}
|
|
|
|
$candidates = @(
|
|
(Join-Path $PSScriptRoot 'ExternalCommandSupport.psm1')
|
|
)
|
|
foreach ($modulePath in $candidates) {
|
|
if (Test-Path -LiteralPath $modulePath -PathType Leaf) {
|
|
Import-Module $modulePath -Force -Global
|
|
break
|
|
}
|
|
}
|
|
|
|
if (-not (Get-Command Invoke-ExternalCommand -ErrorAction SilentlyContinue)) {
|
|
throw "ExternalCommandSupport module not found. Expected: $($candidates -join ', ')"
|
|
}
|
|
}
|
|
|
|
function Import-LoggingModuleInternal {
|
|
if (Get-Command Write-Log -ErrorAction SilentlyContinue) {
|
|
return
|
|
}
|
|
|
|
$modulePath = Join-Path $PSScriptRoot "Logging.psm1"
|
|
if (Test-Path $modulePath) {
|
|
Import-Module $modulePath -Force
|
|
}
|
|
}
|
|
|
|
function Write-TestRunnerLogInternal {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Message,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[ValidateSet("INFO", "OK", "WARN", "ERROR", "STEP", "DEBUG")]
|
|
[string]$Level = "INFO"
|
|
)
|
|
|
|
Import-LoggingModuleInternal
|
|
|
|
if (Get-Command Write-Log -ErrorAction SilentlyContinue) {
|
|
Write-Log -Level $Level -Message $Message
|
|
return
|
|
}
|
|
|
|
Write-Host $Message -ForegroundColor Gray
|
|
}
|
|
|
|
function Invoke-TestsWithCoverage {
|
|
<#
|
|
.SYNOPSIS
|
|
Runs unit tests with code coverage and returns coverage metrics.
|
|
|
|
.PARAMETER TestProjectPath
|
|
One or more paths to test project directories (or .csproj files). Each project
|
|
is tested in order; coverage metrics are aggregated across all Cobertura outputs.
|
|
|
|
.PARAMETER Silent
|
|
Suppress console output (for JSON consumption).
|
|
|
|
.PARAMETER ResultsDirectory
|
|
Optional fixed directory where test result files are written.
|
|
|
|
.PARAMETER KeepResults
|
|
Keep the TestResults folder after execution.
|
|
|
|
.OUTPUTS
|
|
PSCustomObject with properties:
|
|
- Success: bool
|
|
- Error: string (if failed)
|
|
- LineRate: double
|
|
- BranchRate: double
|
|
- MethodRate: double
|
|
- TotalMethods: int
|
|
- CoveredMethods: int
|
|
- CoverageFile: string (ReportGenerator reports arg: one file, or semicolon-separated)
|
|
- CoverageFiles: string[] (individual Cobertura paths)
|
|
- ResultsDirectory: string (absolute folder used for dotnet test output; may be removed after run unless -KeepResults)
|
|
|
|
.EXAMPLE
|
|
$result = Invoke-TestsWithCoverage -TestProjectPath ".\Tests"
|
|
if ($result.Success) { Write-TestRunnerLogInternal -Level "INFO" -Message "Line coverage: $($result.LineRate)%" }
|
|
|
|
.EXAMPLE
|
|
$result = Invoke-TestsWithCoverage -TestProjectPath @(".\ProjA.Tests", ".\ProjB.Tests")
|
|
#>
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string[]]$TestProjectPath,
|
|
|
|
[switch]$Silent,
|
|
|
|
[string]$ResultsDirectory,
|
|
|
|
[switch]$KeepResults
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
# Normalize to a non-empty list of absolute working directories (folder containing the test project).
|
|
$resolvedProjectDirs = [System.Collections.Generic.List[string]]::new()
|
|
foreach ($raw in $TestProjectPath) {
|
|
if ([string]::IsNullOrWhiteSpace($raw)) { continue }
|
|
$full = [System.IO.Path]::GetFullPath($raw.Trim())
|
|
if (-not (Test-Path $full)) {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "Test project not found at: $raw"
|
|
}
|
|
}
|
|
$item = Get-Item -LiteralPath $full
|
|
$dir = if ($item.PSIsContainer) { $item.FullName } else { $item.Directory.FullName }
|
|
if ($resolvedProjectDirs -notcontains $dir) {
|
|
[void]$resolvedProjectDirs.Add($dir)
|
|
}
|
|
}
|
|
|
|
if ($resolvedProjectDirs.Count -eq 0) {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "No valid test project paths were provided."
|
|
}
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($ResultsDirectory)) {
|
|
$ResultsDir = Join-Path $resolvedProjectDirs[0] "TestResults"
|
|
}
|
|
else {
|
|
$ResultsDir = [System.IO.Path]::GetFullPath($ResultsDirectory)
|
|
}
|
|
|
|
# Clean previous results once (shared output for all test runs).
|
|
if (Test-Path $ResultsDir) {
|
|
Remove-Item -Recurse -Force $ResultsDir
|
|
}
|
|
New-Item -ItemType Directory -Path $ResultsDir -Force | Out-Null
|
|
|
|
if (-not $Silent) {
|
|
Write-TestRunnerLogInternal -Level "STEP" -Message "Running tests with code coverage..."
|
|
foreach ($d in $resolvedProjectDirs) {
|
|
Write-TestRunnerLogInternal -Level "INFO" -Message "Test Project: $d"
|
|
}
|
|
}
|
|
|
|
foreach ($TestProjectDir in $resolvedProjectDirs) {
|
|
Push-Location $TestProjectDir
|
|
try {
|
|
$dotnetArgs = @(
|
|
"test"
|
|
"--collect:XPlat Code Coverage"
|
|
"--results-directory", $ResultsDir
|
|
"--verbosity", $(if ($Silent) { "quiet" } else { "normal" })
|
|
)
|
|
|
|
Import-ExternalCommandSupportInternal
|
|
if ($Silent) {
|
|
$null = Invoke-ExternalCommand -Name dotnet -ArgumentList $dotnetArgs -MergeErrorOutput -ThrowOnError:$false
|
|
}
|
|
else {
|
|
Invoke-ExternalCommand -Name dotnet -ArgumentList $dotnetArgs -ThrowOnError:$false | Out-Default
|
|
}
|
|
|
|
$testExitCode = $LASTEXITCODE
|
|
if ($testExitCode -ne 0) {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "Tests failed in '$TestProjectDir' with exit code $testExitCode"
|
|
}
|
|
}
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
$coverageFiles = @(Get-ChildItem -Path $ResultsDir -Filter "coverage.cobertura.xml" -Recurse | Sort-Object FullName)
|
|
|
|
if ($coverageFiles.Count -eq 0) {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "Coverage file not found under: $ResultsDir"
|
|
}
|
|
}
|
|
|
|
if (-not $Silent) {
|
|
foreach ($cf in $coverageFiles) {
|
|
Write-TestRunnerLogInternal -Level "OK" -Message "Coverage file found: $($cf.FullName)"
|
|
}
|
|
Write-TestRunnerLogInternal -Level "STEP" -Message "Parsing coverage data..."
|
|
}
|
|
|
|
# Aggregate line/branch from Cobertura counters; methods by walking all files.
|
|
$linesCoveredTotal = 0L
|
|
$linesValidTotal = 0L
|
|
$branchesCoveredTotal = 0L
|
|
$branchesValidTotal = 0L
|
|
$totalMethods = 0
|
|
$coveredMethods = 0
|
|
|
|
foreach ($cf in $coverageFiles) {
|
|
[xml]$coverageXml = Get-Content -LiteralPath $cf.FullName -Raw
|
|
$root = $coverageXml.coverage
|
|
$lcAttr = $root.'lines-covered'
|
|
$lvAttr = $root.'lines-valid'
|
|
if ($null -ne $lcAttr -and $null -ne $lvAttr -and [long]$lvAttr -gt 0) {
|
|
$linesCoveredTotal += [long]$lcAttr
|
|
$linesValidTotal += [long]$lvAttr
|
|
}
|
|
|
|
$bcAttr = $root.'branches-covered'
|
|
$bvAttr = $root.'branches-valid'
|
|
if ($null -ne $bcAttr -and $null -ne $bvAttr -and [long]$bvAttr -gt 0) {
|
|
$branchesCoveredTotal += [long]$bcAttr
|
|
$branchesValidTotal += [long]$bvAttr
|
|
}
|
|
|
|
foreach ($package in @($root.packages.package)) {
|
|
foreach ($class in @($package.classes.class)) {
|
|
$methodNodes = $class.methods
|
|
if ($null -eq $methodNodes) { continue }
|
|
foreach ($method in @($methodNodes.method)) {
|
|
if ($null -eq $method) { continue }
|
|
$totalMethods++
|
|
if ([double]$method.'line-rate' -gt 0) {
|
|
$coveredMethods++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($linesValidTotal -gt 0) {
|
|
$lineRate = [math]::Round(($linesCoveredTotal / $linesValidTotal) * 100, 1)
|
|
}
|
|
else {
|
|
# Fallback: average of per-file line-rate when counters are missing (older Cobertura).
|
|
$acc = 0.0
|
|
$n = 0
|
|
foreach ($cf in $coverageFiles) {
|
|
[xml]$coverageXml = Get-Content -LiteralPath $cf.FullName -Raw
|
|
$acc += [double]$coverageXml.coverage.'line-rate'
|
|
$n++
|
|
}
|
|
$lineRate = [math]::Round(($acc / [math]::Max($n, 1)) * 100, 1)
|
|
}
|
|
|
|
if ($branchesValidTotal -gt 0) {
|
|
$branchRate = [math]::Round(($branchesCoveredTotal / $branchesValidTotal) * 100, 1)
|
|
}
|
|
else {
|
|
$acc = 0.0
|
|
$n = 0
|
|
foreach ($cf in $coverageFiles) {
|
|
[xml]$coverageXml = Get-Content -LiteralPath $cf.FullName -Raw
|
|
$acc += [double]$coverageXml.coverage.'branch-rate'
|
|
$n++
|
|
}
|
|
$branchRate = [math]::Round(($acc / [math]::Max($n, 1)) * 100, 1)
|
|
}
|
|
|
|
$methodRate = if ($totalMethods -gt 0) { [math]::Round(($coveredMethods / $totalMethods) * 100, 1) } else { 0 }
|
|
|
|
$coveragePaths = @($coverageFiles | ForEach-Object { $_.FullName })
|
|
$coverageFileReportArg = $coveragePaths -join ";"
|
|
$resultsDirectoryFull = [System.IO.Path]::GetFullPath($ResultsDir)
|
|
|
|
# Cleanup unless KeepResults is specified
|
|
if (-not $KeepResults) {
|
|
if (Test-Path $ResultsDir) {
|
|
Remove-Item -Recurse -Force $ResultsDir
|
|
}
|
|
}
|
|
|
|
# Return results
|
|
return [PSCustomObject]@{
|
|
Success = $true
|
|
LineRate = $lineRate
|
|
BranchRate = $branchRate
|
|
MethodRate = $methodRate
|
|
TotalMethods = $totalMethods
|
|
CoveredMethods = $coveredMethods
|
|
CoverageFile = $coverageFileReportArg
|
|
CoverageFiles = $coveragePaths
|
|
ResultsDirectory = $resultsDirectoryFull
|
|
}
|
|
}
|
|
|
|
function Invoke-NpmJestTestsWithCoverage {
|
|
<#
|
|
.SYNOPSIS
|
|
Runs npm/Jest tests with coverage and returns normalized metrics.
|
|
|
|
.PARAMETER WorkspaceRoot
|
|
npm workspace root (folder containing package.json and jest.config).
|
|
|
|
.PARAMETER TestScript
|
|
npm script name to run (default: test). Coverage flags are appended via `--`.
|
|
|
|
.PARAMETER CoverageDirectory
|
|
Relative path under WorkspaceRoot where Jest writes coverage output.
|
|
|
|
.PARAMETER Silent
|
|
Suppress console output from npm.
|
|
|
|
.OUTPUTS
|
|
Same metric shape as Invoke-TestsWithCoverage, plus CoverageSummaryFile when available.
|
|
#>
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$WorkspaceRoot,
|
|
|
|
[string]$TestScript = 'test',
|
|
|
|
[string]$CoverageDirectory = 'coverage',
|
|
|
|
[switch]$Silent
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$workspaceFull = [System.IO.Path]::GetFullPath($WorkspaceRoot)
|
|
if (-not (Test-Path (Join-Path $workspaceFull 'package.json') -PathType Leaf)) {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "package.json not found in workspace root: $workspaceFull"
|
|
}
|
|
}
|
|
|
|
if (-not $Silent) {
|
|
Write-TestRunnerLogInternal -Level 'STEP' -Message 'Running npm/Jest tests with coverage...'
|
|
Write-TestRunnerLogInternal -Level 'INFO' -Message "Workspace: $workspaceFull"
|
|
}
|
|
|
|
Push-Location $workspaceFull
|
|
try {
|
|
$npmArgs = @('run', $TestScript, '--', '--coverage', '--coverageReporters=json-summary', '--coverageReporters=text')
|
|
Import-ExternalCommandSupportInternal
|
|
if ($Silent) {
|
|
$null = Invoke-ExternalCommand -Name npm -ArgumentList $npmArgs -MergeErrorOutput -ThrowOnError:$false
|
|
}
|
|
else {
|
|
Invoke-ExternalCommand -Name npm -ArgumentList $npmArgs -ThrowOnError:$false | Out-Default
|
|
}
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "npm run $TestScript failed with exit code $LASTEXITCODE"
|
|
}
|
|
}
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
|
|
$summaryPath = Join-Path $workspaceFull (Join-Path $CoverageDirectory 'coverage-summary.json')
|
|
if (-not (Test-Path $summaryPath -PathType Leaf)) {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "Jest coverage summary not found at: $summaryPath"
|
|
}
|
|
}
|
|
|
|
$summaryJson = Get-Content -LiteralPath $summaryPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
$total = $summaryJson.total
|
|
if ($null -eq $total) {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "Jest coverage summary is missing 'total' metrics in: $summaryPath"
|
|
}
|
|
}
|
|
|
|
$lineRate = [math]::Round([double]$total.lines.pct, 1)
|
|
$branchRate = [math]::Round([double]$total.branches.pct, 1)
|
|
$methodRate = [math]::Round([double]$total.functions.pct, 1)
|
|
$totalMethods = [int]$total.functions.total
|
|
$coveredMethods = [int]$total.functions.covered
|
|
$resultsDirectory = [System.IO.Path]::GetFullPath((Join-Path $workspaceFull $CoverageDirectory))
|
|
|
|
if (-not $Silent) {
|
|
Write-TestRunnerLogInternal -Level 'OK' -Message "Coverage summary: $summaryPath"
|
|
}
|
|
|
|
return [PSCustomObject]@{
|
|
Success = $true
|
|
LineRate = $lineRate
|
|
BranchRate = $branchRate
|
|
MethodRate = $methodRate
|
|
TotalMethods = $totalMethods
|
|
CoveredMethods = $coveredMethods
|
|
CoverageSummaryFile = $summaryPath
|
|
ResultsDirectory = $resultsDirectory
|
|
}
|
|
}
|
|
|
|
function Get-NpmCoverageFromSummaryFile {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$SummaryPath,
|
|
|
|
[switch]$Silent
|
|
)
|
|
|
|
if (-not (Test-Path -LiteralPath $SummaryPath -PathType Leaf)) {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "Jest coverage summary not found at: $SummaryPath"
|
|
}
|
|
}
|
|
|
|
$summaryJson = Get-Content -LiteralPath $SummaryPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
$total = $summaryJson.total
|
|
if ($null -eq $total) {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "Jest coverage summary is missing 'total' metrics in: $SummaryPath"
|
|
}
|
|
}
|
|
|
|
return [PSCustomObject]@{
|
|
Success = $true
|
|
LineRate = [math]::Round([double]$total.lines.pct, 1)
|
|
BranchRate = [math]::Round([double]$total.branches.pct, 1)
|
|
MethodRate = [math]::Round([double]$total.functions.pct, 1)
|
|
TotalMethods = [int]$total.functions.total
|
|
CoveredMethods = [int]$total.functions.covered
|
|
CoverageFormat = 'npm'
|
|
CoverageSummaryFile = $SummaryPath
|
|
}
|
|
}
|
|
|
|
function Get-DotNetCoverageFromResultsDirectory {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ResultsDirectory,
|
|
|
|
[switch]$Silent
|
|
)
|
|
|
|
$coverageFiles = @(Get-ChildItem -Path $ResultsDirectory -Filter 'coverage.cobertura.xml' -Recurse -ErrorAction SilentlyContinue | Sort-Object FullName)
|
|
if ($coverageFiles.Count -eq 0) {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "Coverage file not found under: $ResultsDirectory"
|
|
}
|
|
}
|
|
|
|
$linesCoveredTotal = 0L
|
|
$linesValidTotal = 0L
|
|
$branchesCoveredTotal = 0L
|
|
$branchesValidTotal = 0L
|
|
$totalMethods = 0
|
|
$coveredMethods = 0
|
|
|
|
foreach ($cf in $coverageFiles) {
|
|
[xml]$coverageXml = Get-Content -LiteralPath $cf.FullName -Raw
|
|
$root = $coverageXml.coverage
|
|
$lcAttr = $root.'lines-covered'
|
|
$lvAttr = $root.'lines-valid'
|
|
if ($null -ne $lcAttr -and $null -ne $lvAttr -and [long]$lvAttr -gt 0) {
|
|
$linesCoveredTotal += [long]$lcAttr
|
|
$linesValidTotal += [long]$lvAttr
|
|
}
|
|
|
|
$bcAttr = $root.'branches-covered'
|
|
$bvAttr = $root.'branches-valid'
|
|
if ($null -ne $bcAttr -and $null -ne $bvAttr -and [long]$bvAttr -gt 0) {
|
|
$branchesCoveredTotal += [long]$bcAttr
|
|
$branchesValidTotal += [long]$bvAttr
|
|
}
|
|
|
|
foreach ($package in @($root.packages.package)) {
|
|
foreach ($class in @($package.classes.class)) {
|
|
$methodNodes = $class.methods
|
|
if ($null -eq $methodNodes) { continue }
|
|
foreach ($method in @($methodNodes.method)) {
|
|
if ($null -eq $method) { continue }
|
|
$totalMethods++
|
|
if ([double]$method.'line-rate' -gt 0) {
|
|
$coveredMethods++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($linesValidTotal -gt 0) {
|
|
$lineRate = [math]::Round(($linesCoveredTotal / $linesValidTotal) * 100, 1)
|
|
}
|
|
else {
|
|
$acc = 0.0
|
|
$n = 0
|
|
foreach ($cf in $coverageFiles) {
|
|
[xml]$coverageXml = Get-Content -LiteralPath $cf.FullName -Raw
|
|
$acc += [double]$coverageXml.coverage.'line-rate'
|
|
$n++
|
|
}
|
|
$lineRate = [math]::Round(($acc / [math]::Max($n, 1)) * 100, 1)
|
|
}
|
|
|
|
if ($branchesValidTotal -gt 0) {
|
|
$branchRate = [math]::Round(($branchesCoveredTotal / $branchesValidTotal) * 100, 1)
|
|
}
|
|
else {
|
|
$acc = 0.0
|
|
$n = 0
|
|
foreach ($cf in $coverageFiles) {
|
|
[xml]$coverageXml = Get-Content -LiteralPath $cf.FullName -Raw
|
|
$acc += [double]$coverageXml.coverage.'branch-rate'
|
|
$n++
|
|
}
|
|
$branchRate = [math]::Round(($acc / [math]::Max($n, 1)) * 100, 1)
|
|
}
|
|
|
|
$methodRate = if ($totalMethods -gt 0) { [math]::Round(($coveredMethods / $totalMethods) * 100, 1) } else { 0 }
|
|
$coveragePaths = @($coverageFiles | ForEach-Object { $_.FullName })
|
|
|
|
return [PSCustomObject]@{
|
|
Success = $true
|
|
LineRate = $lineRate
|
|
BranchRate = $branchRate
|
|
MethodRate = $methodRate
|
|
TotalMethods = $totalMethods
|
|
CoveredMethods = $coveredMethods
|
|
CoverageFile = ($coveragePaths -join ';')
|
|
CoverageFiles = $coveragePaths
|
|
ResultsDirectory = [System.IO.Path]::GetFullPath($ResultsDirectory)
|
|
CoverageFormat = 'dotnet'
|
|
}
|
|
}
|
|
|
|
function Get-CoverageFromResultsDirectory {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ResultsDirectory,
|
|
|
|
[ValidateSet('auto', 'dotnet', 'npm')]
|
|
[string]$Format = 'auto',
|
|
|
|
[switch]$Silent
|
|
)
|
|
|
|
$resolvedDirectory = [System.IO.Path]::GetFullPath($ResultsDirectory)
|
|
if (-not (Test-Path -LiteralPath $resolvedDirectory -PathType Container)) {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "Results directory not found: $resolvedDirectory"
|
|
}
|
|
}
|
|
|
|
$hasDotNet = @(Get-ChildItem -Path $resolvedDirectory -Filter 'coverage.cobertura.xml' -Recurse -ErrorAction SilentlyContinue).Count -gt 0
|
|
$jestSummary = Join-Path $resolvedDirectory 'coverage-summary.json'
|
|
$hasNpm = Test-Path -LiteralPath $jestSummary -PathType Leaf
|
|
|
|
$effectiveFormat = $Format
|
|
if ($effectiveFormat -eq 'auto') {
|
|
if ($hasDotNet -and -not $hasNpm) {
|
|
$effectiveFormat = 'dotnet'
|
|
}
|
|
elseif ($hasNpm -and -not $hasDotNet) {
|
|
$effectiveFormat = 'npm'
|
|
}
|
|
elseif ($hasDotNet) {
|
|
$effectiveFormat = 'dotnet'
|
|
}
|
|
elseif ($hasNpm) {
|
|
$effectiveFormat = 'npm'
|
|
}
|
|
else {
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "Coverage file not found under: $resolvedDirectory"
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($effectiveFormat -eq 'dotnet') {
|
|
return Get-DotNetCoverageFromResultsDirectory -ResultsDirectory $resolvedDirectory -Silent:$Silent
|
|
}
|
|
|
|
if ($effectiveFormat -eq 'npm') {
|
|
$npmResult = Get-NpmCoverageFromSummaryFile -SummaryPath $jestSummary -Silent:$Silent
|
|
if (-not $npmResult.Success) {
|
|
return $npmResult
|
|
}
|
|
|
|
$npmResult | Add-Member -NotePropertyName ResultsDirectory -NotePropertyValue $resolvedDirectory -Force
|
|
return $npmResult
|
|
}
|
|
|
|
return [PSCustomObject]@{
|
|
Success = $false
|
|
Error = "Unsupported coverage format '$Format'."
|
|
}
|
|
}
|
|
|
|
function Publish-CoverageMetricsToSharedContext {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
$SharedSettings,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
$TestResult
|
|
)
|
|
|
|
if (-not (Get-Command Set-EngineFact -ErrorAction SilentlyContinue)) {
|
|
$engineContextPath = Join-Path $PSScriptRoot 'Engine' 'EngineContext.psm1'
|
|
if (Test-Path -LiteralPath $engineContextPath -PathType Leaf) {
|
|
Import-Module $engineContextPath -Force -Global
|
|
}
|
|
}
|
|
|
|
if (Get-Command Set-EngineFact -ErrorAction SilentlyContinue) {
|
|
Set-EngineFact -Context $SharedSettings -Namespace 'test' -Name 'testResult' -Value $TestResult -Overwrite Replace -LegacyProperty 'testResult'
|
|
Set-EngineFact -Context $SharedSettings -Namespace 'test' -Name 'coverageLineRate' -Value $TestResult.LineRate -Overwrite Replace -LegacyProperty 'coverageLineRate'
|
|
Set-EngineFact -Context $SharedSettings -Namespace 'test' -Name 'qualityLineCoverage' -Value $TestResult.LineRate -Overwrite Replace -LegacyProperty 'qualityLineCoverage'
|
|
Set-EngineFact -Context $SharedSettings -Namespace 'test' -Name 'coverageBranchRate' -Value $TestResult.BranchRate -Overwrite Replace -LegacyProperty 'coverageBranchRate'
|
|
Set-EngineFact -Context $SharedSettings -Namespace 'test' -Name 'coverageMethodRate' -Value $TestResult.MethodRate -Overwrite Replace -LegacyProperty 'coverageMethodRate'
|
|
Set-EngineFact -Context $SharedSettings -Namespace 'test' -Name 'coverageTotalMethods' -Value $TestResult.TotalMethods -Overwrite Replace -LegacyProperty 'coverageTotalMethods'
|
|
Set-EngineFact -Context $SharedSettings -Namespace 'test' -Name 'coverageCoveredMethods' -Value $TestResult.CoveredMethods -Overwrite Replace -LegacyProperty 'coverageCoveredMethods'
|
|
|
|
if ($TestResult.PSObject.Properties.Name -contains 'CoverageFormat') {
|
|
Set-EngineFact -Context $SharedSettings -Namespace 'test' -Name 'coverageFormat' -Value $TestResult.CoverageFormat -Overwrite Replace -LegacyProperty 'coverageFormat'
|
|
}
|
|
|
|
if (($TestResult.PSObject.Properties.Name -contains 'ResultsDirectory') -and $TestResult.ResultsDirectory) {
|
|
Set-EngineFact -Context $SharedSettings -Namespace 'test' -Name 'testResultsDirectory' -Value $TestResult.ResultsDirectory -Overwrite Replace -LegacyProperty 'testResultsDirectory'
|
|
}
|
|
|
|
if ($TestResult.CoverageFiles) {
|
|
Set-EngineFact -Context $SharedSettings -Namespace 'test' -Name 'coverageCoberturaPaths' -Value @($TestResult.CoverageFiles) -Overwrite Replace -LegacyProperty 'coverageCoberturaPaths'
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
$SharedSettings | Add-Member -NotePropertyName testResult -NotePropertyValue $TestResult -Force
|
|
$SharedSettings | Add-Member -NotePropertyName qualityLineCoverage -NotePropertyValue $TestResult.LineRate -Force
|
|
$SharedSettings | Add-Member -NotePropertyName coverageLineRate -NotePropertyValue $TestResult.LineRate -Force
|
|
$SharedSettings | Add-Member -NotePropertyName coverageBranchRate -NotePropertyValue $TestResult.BranchRate -Force
|
|
$SharedSettings | Add-Member -NotePropertyName coverageMethodRate -NotePropertyValue $TestResult.MethodRate -Force
|
|
$SharedSettings | Add-Member -NotePropertyName coverageTotalMethods -NotePropertyValue $TestResult.TotalMethods -Force
|
|
$SharedSettings | Add-Member -NotePropertyName coverageCoveredMethods -NotePropertyValue $TestResult.CoveredMethods -Force
|
|
|
|
if ($TestResult.PSObject.Properties.Name -contains 'CoverageFormat') {
|
|
$SharedSettings | Add-Member -NotePropertyName coverageFormat -NotePropertyValue $TestResult.CoverageFormat -Force
|
|
}
|
|
|
|
if (($TestResult.PSObject.Properties.Name -contains 'ResultsDirectory') -and $TestResult.ResultsDirectory) {
|
|
$SharedSettings | Add-Member -NotePropertyName testResultsDirectory -NotePropertyValue $TestResult.ResultsDirectory -Force
|
|
}
|
|
|
|
if ($TestResult.CoverageFiles) {
|
|
$SharedSettings | Add-Member -NotePropertyName coverageCoberturaPaths -NotePropertyValue @($TestResult.CoverageFiles) -Force
|
|
}
|
|
}
|
|
|
|
Export-ModuleMember -Function `
|
|
Invoke-TestsWithCoverage, `
|
|
Invoke-NpmJestTestsWithCoverage, `
|
|
Get-NpmCoverageFromSummaryFile, `
|
|
Get-DotNetCoverageFromResultsDirectory, `
|
|
Get-CoverageFromResultsDirectory, `
|
|
Publish-CoverageMetricsToSharedContext
|