Public/Format-Pester.ps1

Function Format-Pester
{
<#
    .SYNOPSIS
    Document Pester's tests results into the selected format (HTML, Word, Text).
    
    .DESCRIPTION
    Create documents in formats: HTML, Word, Text using PScribo PowerShell module. Documents are preformated to be human friendly.
    Local Word installation is not needed to be installed on the computers were documents
     
    .PARAMETER PesterResult
    Specifies the Pester results Object
         
    .PARAMETER Format
    Specifies the document format. Might be:
    - Text
    - HTML
    - Word
     
    .PARAMETER Path
    Specifies where the documents will be stored. Default is the path where is executed this function.
     
    .PARAMETER BaseFileName
    Specifies the document name. Default is 'Pester_Results'.
     
    .PARAMETER GroupResultsBy
    Select how results should be groupped. Available options: Result, Result-Describe, Result-Describe-Context.
     
    .PARAMETER PassedOnly
    Select to return information about passed tests only.
     
    .PARAMETER FailedOnly
    Select to return information about failed tests only.
     
    .PARAMETER SummaryOnly
    Select to return only summaries for tests only (sums of numbers passed/failed/etc. tests).
     
    .PARAMETER SkipTableOfContent
    Select to skip adding table of content at the begining of document(s).
         
    .PARAMETER SkipSummary
    Select to skip adding table with test summaries (sums of numbers passed/failed/etc. tests).
     
    .EXAMPLE
    Invoke-Pester -PassThru | Format-Pester -Path . -Format HTML,Word,Text -BaseFileName 'PesterResults'
 
    This command will document the results of the Pester's tests. Documents will be stored in the current path and they will be available in 3 formats (.html,.docx and .txt).
     
    .LINK
    https://github.com/equelin/Format-Pester
         
    .NOTES
    Initial author: Erwan Quelin
     
    Credits/coauthors:
    Travis Plunk, github[at]ez13[dot]net
    Wojciech Sciesinski, wojciech[at]sciesinski[dot]net
     
    LICENSE
    Licensed under the MIT License - https://github.com/equelin/Format-Pester/blob/master/LICENSE
         
    TODO
    - add alligning of width for tables if grouping in used
    - Pester test need to be updated - yes, post factum TDD ;-)
     
     
  #>

    
    [CmdletBinding(DefaultParameterSetName = 'AllParamSet')]
    [OutputType([System.IO.FileInfo])]
    Param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $True, ValueFromPipelinebyPropertyName = $True, HelpMessage = 'Pester results Object', ParameterSetName = 'AllParamSet')]
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $True, ValueFromPipelinebyPropertyName = $True, HelpMessage = 'Pester results Object', ParameterSetName = 'PassedOnlyParamSet')]
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $True, ValueFromPipelinebyPropertyName = $True, HelpMessage = 'Pester results Object', ParameterSetName = 'FailedOnlyParamSet')]
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $True, ValueFromPipelinebyPropertyName = $True, HelpMessage = 'Pester results Object', ParameterSetName = 'SummaryOnlyParamSet')]
        [ValidateNotNullorEmpty()]
        [Array]$PesterResult,
        [Parameter(Mandatory = $true, HelpMessage = 'PScribo export format', ParameterSetName = 'AllParamSet')]
        [Parameter(Mandatory = $true, ParameterSetName = 'PassedOnlyParamSet')]
        [Parameter(Mandatory = $true, ParameterSetName = 'FailedOnlyParamSet')]
        [Parameter(Mandatory = $true, ParameterSetName = 'SummaryOnlyParamSet')]
        [ValidateSet('Text', 'Word', 'HTML')]
        [String[]]$Format,
        [Parameter(Mandatory = $false, HelpMessage = 'PScribo export path', ParameterSetName = 'AllParamSet')]
        [Parameter(Mandatory = $false, ParameterSetName = 'PassedOnlyParamSet')]
        [Parameter(Mandatory = $false, ParameterSetName = 'FailedOnlyParamSet')]
        [Parameter(Mandatory = $false, ParameterSetName = 'SummaryOnlyParamSet')]
        [ValidateNotNullorEmpty()]
        [String]$Path = (Get-Location -PSProvider FileSystem),
        [ValidateNotNullorEmpty()]
        [string]$BaseFileName = 'Pester_Results',
        [Parameter(Mandatory = $false, ParameterSetName = 'AllParamSet')]
        [Parameter(Mandatory = $false, ParameterSetName = 'PassedOnlyParamSet')]
        [Parameter(Mandatory = $false, ParameterSetName = 'FailedOnlyParamSet')]
        [ValidateSet('Result', 'Result-Describe', 'Result-Describe-Context')]
        [String]$GroupResultsBy = 'Result',
        [Parameter(Mandatory = $false, ParameterSetName = 'PassedOnlyParamSet')]
        [Switch]$PassedOnly,
        [Parameter(Mandatory = $false, ParameterSetName = 'FailedOnlyParamSet')]
        [Switch]$FailedOnly,
        [Parameter(Mandatory = $false, ParameterSetName = 'SummaryOnlyParamSet')]
        [switch]$SummaryOnly,
        [Parameter(Mandatory = $false, ParameterSetName = 'AllParamSet')]
        [Parameter(Mandatory = $false, ParameterSetName = 'PassedOnlyParamSet')]
        [Parameter(Mandatory = $false, ParameterSetName = 'FailedOnlyParamSet')]
        [Parameter(Mandatory = $false, ParameterSetName = 'SummaryOnlyParamSet')]
        [Switch]$SkipTableOfContent,
        [Parameter(Mandatory = $false, ParameterSetName = 'AllParamSet')]
        [Parameter(Mandatory = $false, ParameterSetName = 'PassedOnlyParamSet')]
        [Parameter(Mandatory = $false, ParameterSetName = 'FailedOnlyParamSet')]
        [Switch]$SkipSummary
        
    )
    
    $exportParams = @{ }
    if ($Format.Count -eq 1 -and $Format -eq 'HTML')
    {
        $exportParams += @{
            Options = @{ NoPageLayoutStyle = $true }
        }
    }
    
    If ($SummaryOnly.IsPresent)
    {
        
        $SkipTableOfContent = $true
        
    }
    
    Document $BaseFileName {
        $defaultColumns = @('TotalCount', 'PassedCount', 'FailedCount', 'SkippedCount', 'PendingCount')
        $defaultHeaders = 'Total Tests', 'Passed Tests', 'Failed Tests', 'Skipped Tests', 'Pending Tests'
        
        # Global options
        GlobalOption -PageSize A4
        
        # Style definitions
        Style -Name Total -Color White -BackgroundColor Blue
        Style -Name Passed -Color White -BackgroundColor Green
        Style -Name Failed -Color White -BackgroundColor Red
        Style -Name Other -Color White -BackgroundColor Gray
        
        If (-not $SkipTableOfContent.ispresent)
        {
            
            # Table of content
            TOC -Name 'Table of Contents'
            
        }
        
        If (-not $SkipSummary.IsPresent)
        {
            
            # Results Summary
            $ValidResults = $PesterResult | Where-Object { $null -ne $_.TotalCount } | Sort-Object -Property FailedCount -Descending
            Section -Style Heading2 'Results summary' {
                
                $ValidResults | Set-Style -Style 'Total' -Property 'TotalCount'
                $ValidResults | Set-Style -Style 'Passed' -Property 'PassedCount'
                $ValidResults | Set-Style -Style 'Failed' -Property 'FailedCount'
                $ValidResults | Set-Style -Style 'Other' -Property 'SkippedCount'
                $ValidResults | Set-Style -Style 'Other' -Property 'PendingCount'
                $ValidResults | Table -Columns $defaultColumns -Headers $defaultHeaders
                
            }
            
        }
        
        $Head1Counter = 1
        $Head2counter = 1
        $Head3counter = 1
        
        $PesterTestsResults = $PesterResult | Select-Object -ExpandProperty TestResult
        
        
        If (-not $SummaryOnly.IsPresent -and -not $PassedOnly.IsPresent -and $PesterResult.FailedCount -gt 0)
        {
            
            $FailedPesterTestsResults = $PesterTestsResults | Where-object -FilterScript { $_.Result -eq 'Failed' }
            
            If ($GroupResultsBy -eq 'Result')
            {
                
                Section -Style Heading2 "$Head2counter.`t Errors details" {
                    
                    $FailedPesterTestsResults |
                    Table -Columns Context, Name, FailureMessage -Headers 'Context', 'Name', 'Failure Message' -Width 0
                    
                }
                
                $Head2counter++
                
            }
            
            Else
            {
                
                Section -Style Heading1 "$Head1Counter.`t Errors" {
                    
                    #Get unique 'Describe' from failed Pester results
                    [Array]$FailedHeaders2 = $FailedPesterTestsResults | Select Describe -Unique
                    
                    # Failed tests results details - Grouped by Describe
                    foreach ($Header2 in $FailedHeaders2)
                    {
                        
                        Write-Verbose -Message "Found failed in Decribe blocks: $FailedHeaders2"
                        
                        $SubHeader2Number = "{0}.{1}.`t" -f $Head1Counter, $Head2counter
                        
                        Section -Style Heading2 "$SubHeader2Number.`t Errors details by Describe block: $($Header2.Describe)" {
                            
                            $FailedPesterTestsResults2 = $FailedPesterTestsResults | Where-Object -FilterScript { $_.Describe -eq $Header2.Describe }
                            
                            If ($GroupResultsBy -eq 'Result-Describe-Context')
                            {
                                
                                [Array]$FailedHeaders3 = $FailedPesterTestsResults2 | Select Context -Unique
                                
                                foreach ($Header3 in $FailedHeaders3)
                                {
                                    
                                    $FailedPesterTestsResults3 = $FailedPesterTestsResults2 | Where-Object -FilterScript { $_.Context -eq $Header3.Context }
                                    
                                    $SubHeader3Number = "{0}.{1}.{2}.`t" -f $Head1Counter, $Head2counter, $Head3counter
                                    
                                    Section -Style Heading3 "$SubHeader3Number Errors details by Context block: $($Header3.Context)" {
                                        
                                        #Paragraph "$($results.Count) test(s) failed:"
                                        
                                        $FailedPesterTestsResults3 |
                                        Table -Columns Context, Name, FailureMessage -Headers 'Context', 'Name', 'Failure Message' -Width 0
                                    }
                                    
                                    $Head3Counter++
                                    
                                }
                                
                            } #$GroupResultsBy -eq 'Result-Describe-Context'
                            Else
                            {
                                
                                $FailedPesterTestsResults2 |
                                Table -Columns Context, Name, FailureMessage -Headers 'Context', 'Name', 'Failure Message' -Width 0
                                
                            }
                            
                        }
                        
                        $Head2counter++
                        
                    } #end foreach ($Header2 in $FailedHeaders2)
                    
                }
                
                $Head1Counter++
                
            } #end $GroupResultsBy -ne 'Result' for Failed
                        
        } #end of creating section of failed tests details
        
        $Head3counter = 1
        
        If (-not $SummaryOnly.IsPresent -and -not $FailedOnly.IsPresent -and $PesterResult.PassedCount -gt 0)
        {
            
            $PassedPesterTestsResults = $PesterTestsResults | Where-object -FilterScript { $_.Result -eq 'Passed' }
            
            If ($GroupResultsBy -eq 'Result')
            {
                
                
                Section -Style Heading2 "$Head2counter. Success details" {
                    
                    $PassedPesterTestsResults |
                    Table -Columns Describe, Context, Name -Headers 'Describe', 'Context', 'Name' -Width 0
                    
                }
                
            }
            Else
            {
                
                Section -Style Heading1 "$Head1Counter.`t Success" {
                    
                    #Get unique 'Describe' from failed Pester results
                    [Array]$PassedHeaders2 = $PassedPesterTestsResults | Select Describe -Unique
                    
                    # Failed tests results details - Grouped by Describe
                    foreach ($Header2 in $PassedHeaders2)
                    {
                        
                        Write-Verbose -Message "Found success in Decribe blocks: $PassedHeaders2"
                        
                        $SubHeader2Number = "{0}.{1}.`t" -f $Head1Counter, $Head2counter
                        
                        Section -Style Heading2 "$SubHeader2Number Success details by Describe block: $($Header2.Describe)" {
                            
                            $PassedPesterTestsResults2 = $PassedPesterTestsResults | Where-Object -FilterScript { $_.Describe -eq $Header2.Describe }
                            
                            If ($GroupResultsBy -eq 'Result-Describe-Context')
                            {
                                
                                [Array]$PassedHeaders3 = $PassedPesterTestsResults2 | Select Context -Unique
                                
                                foreach ($Header3 in $PassedHeaders3)
                                {
                                    
                                    $PassedPesterTestsResults3 = $PassedPesterTestsResults2 | Where-Object -FilterScript { $_.Context -eq $Header3.Context }
                                    
                                    $SubHeader3Number = "{0}.{1}.{2}.`t" -f $Head1Counter, $Head2counter, $Head3counter
                                    
                                    Section -Style Heading3 "$SubHeader3Number Success details by Context block: $($Header3.Context)" {
                                        
                                        #Paragraph "$($results.Count) test(s) failed:"
                                        
                                        $PassedPesterTestsResults3 |
                                        Table -Columns Describe, Context, Name -Headers 'Describe', 'Context', 'Name' -Width 0
                                    }
                                    
                                    $Head3Counter++
                                    
                                }
                                
                            } #$GroupResultsBy -eq 'Result-Describe-Context'
                            Else
                            {
                                
                                $PassedPesterTestsResults2 |
                                Table -Columns Describe, Context, Name -Headers 'Describe', 'Context', 'Name' -Width 0
                                
                            }
                            
                        }
                        
                        $Head2counter++
                        
                    } #end foreach ($Header2 in $PassedHeaders2)
                    
                }
                
            } #end $GroupResultsBy -ne 'Result' for Passed
            
        } #end of creating section of passed tests details
        
        
    } | Export-Document -Path $Path -Format $Format @exportParams
}