internal/ConvertTo-MtMaesterResult.ps1
| 
                                <# .SYNOPSIS Converts Pester results to the Maester test results format which includes additional information. #> function ConvertTo-MtMaesterResult { [CmdletBinding()] param( # The Pester test results returned from Invoke-Pester -PassThru [Parameter(Mandatory = $true)] [psobject] $PesterResults ) function GetTenantName() { if (Test-MtConnection Graph) { $org = Invoke-MtGraphRequest -RelativeUri 'organization' return $org.DisplayName } elseif (Test-MtConnection Teams) { $tenant = Get-CsTenant return $tenant.DisplayName } else { return "TenantName (not connected to Graph)" } } function GetTenantId() { if (Test-MtConnection Graph) { $mgContext = Get-MgContext return $mgContext.TenantId } elseif (Test-MtConnection Teams) { $tenant = Get-CsTenant return $tenant.TenantId } else { return "TenantId (not connected to Graph)" } } function GetAccount() { if (Test-MtConnection Graph) { $mgContext = Get-MgContext return $mgContext.Account #} elseif (Test-MtConnection Teams) { # $tenant = Get-CsTenant #ToValidate: N/A # return $tenant.DisplayName } else { return "Account (not connected to Graph)" } } function GetTestsSorted() { # Show passed and failed tests first by name then show not run tests $activeTests = $PesterResults.Tests | Where-Object { $_.Result -eq 'Passed' -or $_.Result -eq 'Failed' } | Sort-Object -Property Name $inactiveTests = $PesterResults.Tests | Where-Object { $_.Result -ne 'Passed' -and $_.Result -ne 'Failed' } | Sort-Object -Property Name # Convert to array and add, if not when only one object is returned it doesn't create an array with all items. return @($activeTests) + @($inactiveTests) } function GetFormattedDate($date) { if (!$IsCoreCLR) { # Prevent 5.1 date format to json issue return $date.ToString("o") } else { return $date } } function GetMaesterLatestVersion() { if (Get-Command 'Find-Module' -ErrorAction SilentlyContinue) { return (Find-Module -Name Maester).Version } return 'Unknown' } #if(Test-MtConnection Graph) { #ToValidate: Issue with -SkipGraphConnect # $mgContext = Get-MgContext #} #$tenantId = $mgContext.TenantId ?? "Tenant ID (not connected to Graph)" $tenantId = GetTenantId $tenantName = GetTenantName #$account = $mgContext.Account ?? "Account (not connected to Graph)" $account = GetAccount $currentVersion = Get-MtModuleVersion $latestVersion = GetMaesterLatestVersion $mtTests = @() $sortedTests = GetTestsSorted $testIndex = 0 foreach ($test in $sortedTests) { $testIndex++ $name = $test.ExpandedName $testCustomName = $__MtSession.TestResultDetail[$test.ExpandedName].TestTitle if (![string]::IsNullOrEmpty($testCustomName)) { # Use the custom title if it's been provided. $name = $testCustomName } $helpUrl = '' $start = $name.IndexOf("See https") # Get the Help Url from the message and the ID if ($start -gt 0) { $helpUrl = $name.Substring($start + 4).Trim() #Strip away the "See https://maester.dev" part $name = $name.Substring(0, $start).Trim() #Strip away the "See https://maester.dev" part } # Find the first : and use the first part as $testId and remaining as $testTitle # If no : is found or if there are spaces before the first : display a warning that the test name is not in the correct format $titleStart = $name.IndexOf(':') $testId = $name # Default to the full test name if no split is found $testTitle = $name # Default to the full test name if no split is found if ($titleStart -gt 0) { $testId = $name.Substring(0, $titleStart).Trim() $testTitle = $name.Substring($titleStart + 1).Trim() } else { Write-Warning "Test name does not contain a ':' character. Please use the format 'TestId: TestTitle' → $name" } $testResultDetail = $__MtSession.TestResultDetail[$test.ExpandedName] # Add the other test metadata to the test result $testSetting = Get-MtMaesterConfigTestSetting -TestId $testId $severity = $testResultDetail.Severity # Default to the test result severity if ($testSetting -and [string]::IsNullOrEmpty($testSetting.Severity) -eq $false) { # Overwrite the settings if it is set in the config $severity = $testSetting.Severity } # Setting Result to Error, Overwriting the Skipped state if ($testResultDetail.TestSkipped -eq "Error" ) { $result = "Error" } elseif (( $test -and $test.ErrorRecord -and $test.ErrorRecord.Count -gt 0 -and $test.ErrorRecord[0].CategoryInfo -and $test.ErrorRecord[0].CategoryInfo.Reason) -and (@("RuntimeException","ParameterBindingValidationException","HttpRequestException","TaskCanceledException") -Contains $test.ErrorRecord[0].CategoryInfo.Reason )) { Write-Verbose "Setting result=Error $($name) because: $($test.ErrorRecord[0].CategoryInfo.Reason)" $result = "Error" } else { $result = $test.Result } $timeSpanFormat = 'hh\:mm\:ss' $mtTestInfo = [PSCustomObject]@{ Index = $testIndex Id = $testId Title = $testTitle Name = $name HelpUrl = $helpUrl Severity = $severity Tag = @($test.Block.Tag + $test.Tag | Select-Object -Unique) Result = $result ScriptBlock = $test.ScriptBlock.ToString() ScriptBlockFile = $test.ScriptBlock.File ErrorRecord = $test.ErrorRecord Block = $test.Block.ExpandedName Duration = $test.Duration.ToString($timeSpanFormat) ResultDetail = $testResultDetail } $mtTests += $mtTestInfo } # Count all Passed, Failed, Skipped, Error, NotRun and Total results $Recount = [PSCustomObject]@{ FailedCount = 0 PassedCount = 0 SkippedCount = 0 NotRunCount = 0 ErrorCount = 0 TotalCount = 0 } $Recount.FailedCount = @($mtTests | Where-Object { $_.Result -eq 'Failed' }).Count $Recount.PassedCount = @($mtTests | Where-Object { $_.Result -eq 'Passed' }).Count $Recount.ErrorCount = @($mtTests | Where-Object { $_.Result -eq 'Error' }).Count $Recount.SkippedCount = @($mtTests | Where-Object { $_.Result -eq 'Skipped' }).Count $Recount.NotRunCount = @($mtTests | Where-Object { $_.Result -eq 'NotRun' }).Count $Recount.TotalCount = $mtTests.Count Write-Verbose "Recount: $($Recount | Out-String)" $mtBlocks = @() foreach ($container in $PesterResults.Containers) { foreach ($block in $container.Blocks) { $mtBlockInfo = $mtBlocks | Where-Object { $_.Name -eq $block.Name } if ($null -eq $mtBlockInfo) { Write-Verbose "Recalculating block: $($block.Name)" $mtBlockInfo = [PSCustomObject]@{ Name = $block.Name Result = $block.Result FailedCount = @($mtTests | Where-Object { $_.Result -eq 'Failed' -and $_.Block -eq $block.name }).Count PassedCount = @($mtTests | Where-Object { $_.Result -eq 'Passed' -and $_.Block -eq $block.name }).Count ErrorCount = @($mtTests | Where-Object { $_.Result -eq 'Error' -and $_.Block -eq $block.name }).Count SkippedCount = @($mtTests | Where-Object { $_.Result -eq 'Skipped' -and $_.Block -eq $block.name }).Count NotRunCount = @($mtTests | Where-Object { $_.Result -eq 'NotRun' -and $_.Block -eq $block.name }).Count TotalCount = @($mtTests | Where-Object { $_.Block -eq $block.name }).Count Tag = $block.Tag } $mtBlocks += $mtBlockInfo } else { # We already seen and counted all blocks } } } $mtTestResults = [PSCustomObject]@{ Result = $PesterResults.Result FailedCount = $Recount.FailedCount PassedCount = $Recount.PassedCount ErrorCount = $Recount.ErrorCount SkippedCount = $Recount.SkippedCount NotRunCount = $Recount.NotRunCount TotalCount = $Recount.TotalCount ExecutedAt = GetFormattedDate($PesterResults.ExecutedAt) TotalDuration = $PesterResults.Duration.ToString($timeSpanFormat) UserDuration = $PesterResults.UserDuration.ToString($timeSpanFormat) DiscoveryDuration = $PesterResults.DiscoveryDuration.ToString($timeSpanFormat) FrameworkDuration = $PesterResults.FrameworkDuration.ToString($timeSpanFormat) TenantId = $tenantId TenantName = $tenantName Account = $account CurrentVersion = $currentVersion LatestVersion = $latestVersion Tests = $mtTests Blocks = $mtBlocks } return $mtTestResults }  |