Functions/Gherkin.Output.ps1

function ConvertTo-GherkinStepResult {
    param(
        [String] $Name,
        [Nullable[TimeSpan]] $Time,
        [System.Management.Automation.ErrorRecord] $ErrorRecord,
        [switch]$Strict
    )

    $GetMember = $SafeCommands['Get-Member']
    $SelectObject = $SafeCommands['Select-Object']
    $WhereObject = $SafeCommands['Where-Object']

    $StackTraceFormatString = "at <{0}>, {1}: line {2}"

    $StepResult = [PSCustomObject]@{
        Name           = $Name
        Time           = $Time
        FailureMessage = ''
        StackTrace     = ''
        ErrorRecord    = $null
        Success        = $false
        Result         = 'Failed'
    }

    if (-not $ErrorRecord) {
        $StepResult.Result = 'Passed'
        $StepResult.Success = $true
        $StepResult
        return
    }

    # Convert the exception messages
    $Exception = $ErrorRecord.Exception
    $ExceptionLines = @()

    while ($Exception) {
        if ('PesterGherkinStepUndefined','PesterGherkinStepPending' -notcontains $ErrorRecord.FullyQualifiedErrorId) {
            $ExceptionName = "$($Exception.GetType().Name): "
        }

        if ($Exception.Message) {
            $MessageLines = $Exception.Message.Split([string[]]($([System.Environment]::NewLine), "\n", "`n"), [System.StringSplitOptions]::RemoveEmptyEntries)
        }

        if ($ErrorRecord.FullyQualifiedErrorId -ne 'PesterAssertionFailed' -and @($MessageLines).Length -gt 0) {
            $MessageLines[0] = "${ExceptionName}$($MessageLines[0])"
        }

        [array]::Reverse($MessageLines)
        $ExceptionLines += $MessageLines
        $Exception = $Exception.InnerException
    }

    [array]::Reverse($ExceptionLines)

    $StepResult.FailureMessage += $ExceptionLines

    # Convert the Stack Trace, if present (there might be none if we are raising the error ourselves).
    $StackTraceLines = @(
        # For PowerShell v2, the ErrorRecord will not have a ScriptStackTrace property
        if ( -not ($ErrorRecord | & $GetMember -Name ScriptStackTrace) ) {
            $StackTraceFormatString -f 'ScriptBlock', $ErrorRecord.TargetObject.File, $ErrorRecord.TargetObject.Line
        } else {
            # TODO: this is a workaround see https://github.com/pester/Pester/pull/886
            if ($null -ne $ErrorRecord.ScriptStackTrace) {
                $ErrorRecord.ScriptStackTrace.Split([Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries)
            } elseif ($ErrorRecord.TargetObject -and $ErrorRecord.TargetObject.ScriptStackTrace) {
                $ErrorRecord.TargetObject.ScriptStackTrace.Split([Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries)
            }
        }
    )

    # For Gherkin tests, when an assertion fails, the failed assertion function calls show up in the stack
    # trace before the step definition script block because the assertion's called in the ScriptBlock. BUT,
    # we want the ScriptBlock in the stack trace (whith it's attendant line number) to report back to the
    # user. So, here we filter out those lines from the ScriptStackTrace.
    if ($ErrorRecord.FullyQualifiedErrorId -eq 'PesterAssertionFailed' -and (-not $global:PesterDebugPreference_ShowFullErrors)) {
        $StackTraceLines = $StackTraceLines | & $WhereObject {
            $_ -notmatch '^at (Should<End>|Invoke-(Legacy)?Assertion), .*(\\|/)Functions(\\|/)(Assertions(\\|/)Should\.ps1|.*\.ps1): line \d*$'
        }
    }

    # Since we may need to take special action to make the stack trace nice,
    # check here if we're running in "Strict" mode and set the result to Failed if we are.
    if (!$Strict) {
        switch ($ErrorRecord.FullyQualifiedErrorId) {
            'PesterGherkinStepUndefined' { $StepResult.Result = 'Inconclusive'; break }
            'PesterGherkinStepPending'   { $StepResult.Result = 'Pending'; break }
            'PesterGherkinStepSkipped'   { $StepResult.Result = 'Skipped'; break }
        }
    }

    $Count = 0

    # Omit lines which are internal to Pester
    $Patterns = [string[]]@(
        '^at (Invoke-Test|Context|Describe|InModuleScope|Invoke-Pester), .*(\\|/)Functions(\\|/).*\.ps1: line \d*$',
        '^at Assert-MockCalled, .*(\\|/)Functions(\\|/)Mock\.ps1: line \d*$',
        '^at (<ScriptBlock>|Invoke-Gherkin.*), (<No file>|.*(\\|/)Functions(\\|/).*\.ps1): line \d*$'
    )

    foreach ($line in $StackTraceLines) {
        $LineMatchesAPattern = $null -ne ($Patterns | & $WhereObject { $line -match $_ })

        if ($LineMatchesAPattern) {
            break
        }

        $Count ++
    }

    $StepResult.StackTrace += @(
        if (-not ($ExecutionContext.SessionState.PSVariable.GetValue('PesterDebugPreference_ShowFullErrors'))) {
            if ($ErrorRecord.TargetObject -and $ErrorRecord.TargetObject.ContainsKey('FeaturePath')) {
                $FeatureLine = $ErrorRecord.TargetObject.Step.Location.Line
                $FeaturePath = $ErrorRecord.TargetObject.FeaturePath
                @($StackTraceLines | & $SelectObject -First $Count) + @($StackTraceFormatString -f 'Feature', $FeaturePath, $FeatureLine)
            }
        }
    ) -join "`n"

    $StepResult.ErrorRecord = $ErrorRecord

    $StepResult
}

function Write-Background {
    [CmdletBinding()]
    Param (
        [Parameter(Position = 0, Mandatory = $True)]
        [PSObject]$Pester,

        [Parameter(Position = 1, Mandatory = $True, ValueFromPipeline = $True)]
        #[Gherkin.Ast.Background]
        $Background
    )

    process {
        if (-not ($Pester.Show | Has-Flag Context)) {
            return
        }

        $WriteHost = $SafeCommands['Write-Host']

        $Margin = $Script:Reportstrings.Margin * $Script:GherkinIndentationLevel

        $BackgroundText = "${Margin}$($Script:ReportStrings.Background)" -f $Background.Keyword, $Background.Name

        & $WriteHost
        & $WriteHost $BackgroundText -ForegroundColor $Script:ReportTheme.Background
        if ($Background.Description) {
            & $WriteHost $Background.Description -ForegroundColor $Script:ReportTheme.BackgroundDescription
        }
    }
}

function Write-ExampleSet {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $True)]
        [PSObject]$Pester,

        [Parameter(Position = 1, Mandatory = $True, ValueFromPipeline = $True)]
        #[Gherkin.Ast.Examples]
        $ExampleSet
    )

    process {
        if (-not ($Pester.Show | Has-Flag Context)) {
            return
        }

        $WriteHost = $SafeCommands['Write-Host']

        $Margin = $Script:Reportstrings.Margin * $Script:GherkinIndentationLevel++

        $ExampleSetText = "${Margin}$($Script:ReportStrings.Examples)" -f $ExampleSet.Keyword, $ExampleSet.Name

        & $WriteHost
        & $WriteHost $ExampleSetText -ForegroundColor $Script:ReportTheme.Examples
        if ($ExampleSet.Descripion) {
            & $WriteHost $ExampleSet.Descripion -ForegroundColor $Script:ReportTheme.ExamplesDescription
        }

        if (!$ExampleSet.Scenarios[0].Expand) {
            $Margin = $Script:ReportStrings.Margin * $Script:GherkinIndentationLevel

            & $WriteHost "${Margin}|" -ForegroundColor $Script:ReportTheme.TableCellDivider -NoNewLine
            foreach ($cellvalue in ($ExampleSet.TableHeaderRow.Trim('|') -split '\|')) {
                & $WriteHost $cellValue -ForegroundColor $Script:ReportTheme.ScenarioOutlineTableHeaderCell -NoNewLine
                & $WriteHost '|' -ForegroundColor $Script:ReportTheme.TableCellDivider -NoNewLine
            }
            & $WriteHost
        }
    }
}

function Write-Feature {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $True)]
        [PSObject]$Pester,

        [Parameter(Position = 1, Mandatory = $True, ValueFromPipeline = $True)]
        #[Gherkin.Ast.Feature]
        $Feature
    )

    process {
        if (-not ( $Pester.Show | Has-Flag Describe)) {
            return
        }

        $WriteHost = $SafeCommands['Write-Host']
        $FgFeatureName = $Script:ReportTheme.Feature
        $FgFeatureDescription = $Script:ReportTheme.FeatureDescription

        $Margin = $Script:ReportStrings.Margin * $Script:GherkinIndentationLevel++

        $FeatureText = "${Margin}$($Script:ReportStrings.Feature)" -f $Feature.Keyword, $Feature.Name

        & $WriteHost
        & $WriteHost $FeatureText -ForegroundColor $FgFeatureName
        if ($Feature.Description) {
            & $WriteHost $Feature.Description -ForegroundColor $FgFeatureDescription
        }
    }
}

function Write-ScenarioOutline {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $True)]
        [PSObject]$Pester,

        [Parameter(Position = 1, Mandatory = $True, ValueFromPipeline = $True)]
        #[Gherkin.Ast.ScenarioOutline]
        $ScenarioOutline
    )

    process {
        if (-not ( $Pester.Show | Has-Flag Context)) {
            return
        }

        $WriteHost = $SafeCommands['Write-Host']

        $FgName = $Script:ReportTheme.ScenarioOutline
        $FgDescription = $Script:ReportTheme.ScenarioOutlineDescription

        $Margin = $Script:ReportStrings.Margin * $Script:GherkinIndentationLevel++

        $ScenarioOutlineText = "${Margin}$($Script:ReportStrings.ScenarioOutline)" -f $ScenarioOutline.Keyword, $ScenarioOutline.Name

        & $WriteHost
        & $WriteHost $ScenarioOutlineText -ForegroundColor $FgName
        if ($ScenarioOutline.Description) {
            & $WriteHost $ScenarioOutline.Description -ForegroundColor $FgDescription
        }

        $Margin = $Script:ReportStrings.Margin * $Script:GherkinIndentationLevel
        foreach ($Step in $ScenarioOutline.Steps) {
            & $WriteHost "${Margin}$($Step.Keyword.Trim()) $($Step.Text.Trim())" -ForegroundColor $Script:ReportTheme.ScenarioOutlineStep
        }
    }
}

function Write-Scenario {
    param (
        [Parameter(Position = 0, Mandatory = $True)]
        [PSObject]$Pester,

        [Parameter(Position = 1, Mandatory = $True, ValueFromPipeline = $True)]
        #[Gherkin.Ast.Scenario]
        $Scenario
    )

    process {
        if (-not ( $Pester.Show | Has-Flag Context)) {
            return
        }

        $WhereObject = $SafeCommands['Where-Object']
        $WriteHost = $SafeCommands['Write-Host']
        $FgScenarioName = $Script:ReportTheme.Scenario
        $FgScenarioDescription = $Script:ReportTheme.ScenarioDescription

        if (!$Scenario.ExampleSet -or $Scenario.Expand) {
            $Margin = $Script:ReportStrings.Margin * $Script:GherkinIndentationLevel
            $ScenarioText = "${Margin}$($Script:ReportStrings.Scenario)" -f $Scenario.Keyword, $Scenario.Name

            & $WriteHost
            & $WriteHost $ScenarioText -ForegroundColor $FgScenarioName
            if ($Scenario.Description) {
                & $WriteHost $Scenario.Description -ForegroundColor $FgScenarioDescription
            }
        }
        else {
            $FgColor = switch ($Scenario.Result) {
                'Passed' { $Script:ReportTheme.Pass; break }
                'Failed' { $Script:ReportTheme.Fail; break }
                'Skipped' { $Script:ReportTheme.Skipped; break }
                'Pending' { $Script:ReportTheme.Pending; break }
                'Inconclusive' { $Script:ReportTheme.Undefined; break }
            }
            $FgTableSep = $Script:ReportTheme.TableCellDivider

            $Margin = $Script:ReportStrings.Margin * $Script:GherkinIndentationLevel

            & $WriteHost "${Margin}|" -ForegroundColor $FgTableSep -NoNewLine
            foreach ($Cell in ($Scenario.Name.Trim('|') -split '\|')) {
                & $WriteHost $Cell -ForegroundColor $FgColor -NoNewLine
                & $WriteHost '|' -ForegroundColor $FgTableSep -NoNewLine
            }
            & $WriteHost

            if ($Scenario.Result -eq 'Failed') {
                # Find the failing test result belonging to this scenario and show the error/stack trace.
                $Pester.TestResult | & $WhereObject {
                    $ContextNameParts = $_.Context -split '\\'
                    $Result = $ContextNameParts[$ContextNameParts.Length - 1] -eq $Scenario.Name
                    $Result -and $_.Result -eq 'Failed'
                } |
                ForEach-Object {
                    Write-GherkinStepErrorText $_.FailureMessage $_.StackTrace ($Script:GherkinIndentationLevel + 1) $Script:ReportTheme.Fail
                }
            }

        }
    }
}

function Write-GherkinStepResult {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $True)]
        [PSObject]$Pester,

        [Parameter(Position = 1)]
        [Gherkin.Ast.StepArgument]$MultilineArgument,

        [Parameter(Position = 2, Mandatory = $True, ValueFromPipeline = $True)]
        [PSObject]$StepResult
    )

    process {
        $Quiet = $Pester.Show -eq [Pester.OutputTypes]::None
        $OutputType = [Pester.OutputTypes] $StepResult.Result
        $WriteToScreen = $Pester.Show | Has-Flag $OutputType
        $SkipOutput = $Quiet -or (-not $WriteToScreen)

        if ($SkipOutput) { return }

        $WhereObject = $SafeCommands['Where-Object']

        if (-not ($OutputType | Has-Flag 'Default, Summary')) {
            $WriteStepParams = @{
                StepResult = $StepResult
                MultilineArgument = $MultilineArgument
            }

            $ErrorTextParams = @{
                FailureMessage = $StepResult.FailureMessage
                IndentationLevel = $Script:GherkinIndentationLevel + 2
            }

            switch ($StepResult.Result) {
                Passed {
                    $WriteStepParams.StepTextColor = $Script:ReportTheme.Pass
                    $WriteStepParams.StepArgumentColor = $Script:ReportTheme.PassArgument
                    $WriteStepParams.StepDurationColor = $Script:ReportTheme.PassTime

                    Write-GherkinStepText @WriteStepParams

                    break
                }

                Inconclusive {
                    $WriteStepParams.StepTextColor = [ConsoleColor]$Script:ReportTheme.Undefined
                    $WriteStepParams.StepDurationColor = $Script:ReportTheme.UndefinedTime

                    Write-GherkinStepText @WriteStepParams

                    $ErrorTextParams.FailureMessage = ">>> $($ErrorTextParams.FailureMessage)"
                    $ErrorTextParams.StackTrace = $StepResult.StackTrace -split '\r?\n' | & $WhereObject {
                        $ExecutionContext.SessionState.PSVariable.GetValue('PesterDebugPreference_ShowFullErrors') -or $_ -notmatch '^at Set-StepUndefined'
                    }
                    $ErrorTextParams.ForegroundColor = $Script:ReportTheme.Undefined

                    Write-GherkinStepErrorText @ErrorTextParams

                    break
                }

                Pending {
                    $WriteStepParams.StepTextColor = $Script:ReportTheme.Pending
                    $WriteStepParams.StepArgumentColor = $Script:ReportTheme.PendingArgument
                    $WriteStepParams.StepDurationColor = $Script:ReportTheme.PendingTime

                    Write-GherkinStepText @WriteStepParams

                    $ErrorTextParams.StackTrace = $StepResult.StackTrace -split '\r?\n' | & $WhereObject {
                        $ExecutionContext.SessionState.PSVariable.GetValue('PesterdebugPreference_ShowFullErrors') -or $_ -notmatch '^at Set-StepPending'
                    }
                    $ErrorTextParams.ForegroundColor = $Script:ReportTheme.Pending

                    Write-GherkinStepErrortext @ErrorTextParams

                    break
                }

                Skipped {
                    $WriteStepParams.StepTextColor = $Script:ReportTheme.Skipped
                    $WriteStepParams.StepArgumentColor = $Script:ReportTheme.SkippedArgument
                    $WriteStepParams.StepDurationColor = $Script:ReportTheme.SkippedTime

                    Write-GherkinStepText @WriteStepParams

                    if ($ExecutionContext.SessionState.PSVariable.GetValue('PesterDebugPreference_ShowFullErrors')) {
                        $ErrorTextParams.StackTrace = $StepResult.StackTrace -split '\r?\n'
                        $ErrorTextParams.ForeGroundColor = $Script:ReportTheme.Skipped

                        Write-GherkinStepErrorText @ErrorTextParams
                    }

                    break
                }

                Failed {
                    $WriteStepParams.StepTextColor = $Script:ReportTheme.Fail
                    $WriteStepParams.StepArgumentColor = $Script:ReportTheme.FailArgument
                    $WriteStepParams.StepDurationColor = $Script:ReportTheme.FailTime

                    Write-GherkinStepText @WriteStepParams

                    $ErrorTextParams.StackTrace = $StepResult.StackTrace
                    $ErrorTextParams.ForegroundColor = $Script:ReportTheme.Fail

                    Write-GherkinStepErrortext @ErrorTextParams

                    break
                }
            }
        }
    }
}

function Write-GherkinStepErrorText {
    [CmdletBinding()]
    Param (
        [Parameter(Position = 0, Mandatory = $True)]
        [string]$FailureMessage,

        [Parameter(Position = 1, Mandatory = $True)]
        [string[]]$StackTrace,

        [Parameter(Position = 2, Mandatory = $True)]
        [int]$IndentationLevel,

        [Parameter(Position = 3, Mandatory = $True)]
        [ConsoleColor]$ForegroundColor
    )

    Process {
        $ForEachObject = $SafeCommands['ForEach-Object']
        $WriteHost = $SafeCommands['Write-Host']

        $Margin = $Script:ReportStrings.Margin * $IndentationLevel

        $StackTrace | & $ForEachObject -Begin {
            $OutputLines = @($FailureMessage -split '\r?\n' | & $ForEachObject { "${Margin}$_" })
        } -Process {
            $OutputLines += $_ -replace '(?m)^', $Margin
        } -End {
            & $WriteHost "$($OutputLines -join [Environment]::NewLine)" -ForegroundColor $ForegroundColor
        }
    }
}

function Get-GherkinStepTextParts {
    [CmdletBinding()]
    [OutputType([hashtable[]])]
    Param (
        [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True)]
        [PSObject]$StepResult
    )

    Process {
        $StepText = $StepResult.Name
        $StepParameters = $StepResult.Parameters
        $Result = $StepResult.Result

        # Split the step text into pieces, where the pieces are either generic step text, or input to the step
        # definition. This will allow "highlighting" of the replaceable text parameters of the step
        # definition.
        $StepTextParts = [hashtable[]]@()
        if ($Result -ne 'Inconclusive' -and $StepParameters.Keys.Count) {
            $startingFromIndex = 0
            foreach ($v in $StepParameters.Values) {
                $indexOfValue = $StepText.Substring($startingFromIndex).IndexOf($v)

                if ($indexOfValue -lt 0) {
                    $StepTextParts += [hashtable[]]@(@{ Type = 'Text'; Value = $StepText.Substring($startingFromIndex) })
                    $startingFromIndex += $StepTextParts[-1].Value.Length
                    break;
                }

                $StepTextParts += [hashtable[]]@(
                    @{ Type = 'Text'; Value = $StepText.Substring($startingFromIndex, $indexOfValue) },
                    @{ Type = 'Argument'; Value = $v }
                )

                $startingFromIndex += $indexOfValue + $v.Length
            }

            if ($startingfromIndex -lt ($StepText.Length - 1)) {
                $StepTextParts += [hashtable[]]@(@{ Type = 'Text'; Value = $StepText.Substring($startingFromIndex) })
            }
        }
        else {
            $StepTextParts += [hashtable[]]@(@{ Type = 'Text'; Value = $StepText })
        }

        $StepTextParts
    }
}

function Write-GherkinStepText {
    [CmdletBinding()]
    Param (
        [Parameter(Position = 0)]
        [ConsoleColor]$StepTextColor = [ConsoleColor]::Gray,

        [Parameter(Position = 1)]
        [ConsoleColor]$StepArgumentColor = [ConsoleColor]::Gray,

        [Parameter(Position = 2)]
        [ConsoleColor]$StepDurationColor = [ConsoleColor]::DarkGray,

        [Parameter(Position = 3, Mandatory = $True, ValueFromPipeline = $True)]
        [PSObject]$StepResult,

        [Gherkin.Ast.StepArgument]$MultilineArgument
    )

    Process {
        $ForEachObject = $SafeCommands['ForEach-Object']
        $WriteHost = $SafeCommands['Write-Host']
        $Margin = $Script:ReportStrings.Margin * ($Script:GherkinIndentationLevel + 1)

        $StepResult |
        Get-GherkinStepTextParts |
        & $ForEachObject {
            $FgColor = switch ($_.Type) {
                'Text' { $StepTextColor; break }
                'Argument' { $StepArgumentColor; break }
            }

            & $WriteHost -ForegroundColor $FgColor "${Margin}$($_.Value)" -NoNewline

            # After we print the first part, we don't need the indentation anymore.
            $Margin = ''
        }

        & $WriteHost -ForegroundColor $StepDurationColor " $(Get-HumanTime $StepResult.Time.TotalSeconds)"

        if ($MultilineArgument) {
            if ($MultilineArgument -is [Gherkin.Ast.DataTable]) {
                Write-GherkinMultilineArgument $MultilineArgument
            }
            else {
                Write-GherkinMultilineArgument $MultilineArgument $StepTextColor
            }
        }
    }
}

filter Write-GherkinMultilineArgument {
    [CmdletBinding(DefaultParameterSetName = 'DataTable')]
    Param (
        [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True, ParameterSetName = 'DataTable')]
        [Gherkin.Ast.DataTable]$DataTable,

        [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True, ParameterSetName = 'DocString')]
        [Gherkin.Ast.DocString]$DocString,

        [Parameter(Position = 1, Mandatory = $True, ParameterSetName = 'DocString')]
        [ConsoleColor]$ForegroundColor = [ConsoleColor]::Gray
    )

    Process {
        $Margin = $Script:ReportStrings.Margin * ($Script:GherkinIndentationLevel + 2)

        if ($PSCmdlet.ParameterSetName -eq 'DataTable') {
            $FgDiv = $Script:ReportTheme.TableCellDivider
            $FgVal = $Script:ReportTheme.TableCellValue

            $TableColumnWidths = Get-TableColumnWidths $DataTable.Rows

            foreach ($Row in $DataTable.Rows) {
                & $WriteHost -ForegroundColor $FgDiv "${Margin}|" -NoNewline
                for ($ci = 0; $ci -lt $Row.Cells.Length; $ci++) {
                    & $WriteHost -ForegroundColor $FgVal (" {0,$(-$TableColumnWidths[$ci])} " -f $Row.Cells[$ci].Value) -NoNewline
                    & $WriteHost -ForegroundColor $FgDiv '|' -NoNewLine
                }
                & $WriteHost
            }
        }
        else {
            $DocString.Content -split '\r?\n' | ForEach-Object -Begin {
                & $WriteHost -ForegroundColor $ForegroundColor "${Margin}`"`"`""
            } -Process {
                & $WriteHost -ForegroundColor $ForegroundColor "${Margin}${_}"
            } -End {
                & $WriteHost -ForegroundColor $ForegroundColor "${Margin}`"`"`""
            }
        }
    }
}

function Write-GherkinReport {
    [CmdletBinding()]
    Param (
        [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True)]
        [PSObject]$Pester
    )

    begin {
        $WriteHost = $SafeCommands['Write-Host']
    }

    process {
        if (-not ($Pester.Show | Has-Flag Summary)) {
            return
        }

        $Passed    = $Script:ReportTheme.Pass
        $Failure   = $Script:ReportTheme.Fail
        $Skipped   = $Script:ReportTheme.Skipped
        $Pending   = $Script:ReportTheme.Pending
        $Undefined = $Script:ReportTheme.Undefined

        $PassedScenarioCount    = $Pester.PassedScenarios.Count
        $FailedScenarioCount    = $Pester.FailedScenarios.Count
        $PendingScenarioCount   = $Pester.PendingScenarios.Count
        $UndefinedScenarioCount = $Pester.UndefinedScenarios.Count
        $TotalScenarioCount = $PassedScenarioCount + $FailedScenarioCount + $PendingScenarioCount + $UndefinedScenarioCount

        $ScenarioSummaryCounts = [string[]]@($Script:ReportStrings.ScenarioSummary -f $TotalScenarioCount)
        if ($TotalScenarioCount -eq 1) {
            $ScenarioSummaryCounts[0] = $ScenarioSummaryCounts[0] -replace 'scenarios', 'scenario'
        }

        $ScenarioSummaryCounts += @(
            ($Script:ReportStrings.ScenariosFailed    -f $FailedScenarioCount),
            ($Script:ReportStrings.ScenariosUndefined -f $UndefinedScenarioCount),
            ($Script:ReportStrings.ScenariosPending   -f $PendingScenarioCount),
            ($Script:ReportStrings.ScenariosPassed    -f $PassedScenarioCount)
        )

        $ScenarioSummaryData = foreach ($count in $ScenarioSummaryCounts) {
            $null = $count -match '^(?<ScenarioCount>\d+) (?<Result>failed|undefined|skipped|pending|passed|scenarios? \()'
            if ($Matches) {
                switch ($Matches.Result) {
                    failed    { $Foreground = $Failure;                       break }
                    undefined { $Foreground = $Undefined;                     break }
                    pending   { $Foreground = $Pending;                       break }
                    passed    { $Foreground = $Passed;                        break }
                    default   { $Foreground = $Script:ReportTheme.Foreground; break }
                }

                if ($Matches.ScenarioCount -gt 0) {
                    [PSCustomObject]@{ Foreground = $Foreground; Text = $count }
                }
            }
        }

        & $WriteHost
        for ($i = 0; $i -lt $ScenarioSummaryData.Length; $i++) {
            $SummaryData = $ScenarioSummaryData[$i]
            if ($i -eq $ScenarioSummaryData.Length - 1) {
                & $WriteHost ($SummaryData.Text -replace ', ') -ForegroundColor $SummaryData.Foreground -NoNewLine
                & $WriteHost ')' -ForegroundColor $Script:ReportTheme.Foreground
            }
            else {
                & $WriteHost $SummaryData.Text -ForegroundColor $SummaryData.Foreground -NoNewLine
                if ($i) {
                    & $WriteHost ', ' -Foreground $Script:ReportTheme.Foreground -NoNewLine
                }
            }
        }

        $StepSummaryCounts = [string[]]@($Script:ReportStrings.StepsSummary -f $Pester.TotalCount)
        if ($Pester.TotalCount -eq 1) {
            $StepSummaryCounts[0] = $StepSummaryCounts[0] -replace 'steps', 'step'
        }

        $StepSummaryCounts += @(
            ($Script:ReportStrings.StepsFailed    -f $Pester.FailedCount),
            ($Script:ReportStrings.StepsUndefined -f $Pester.InconclusiveCount),
            ($Script:ReportStrings.StepsSkipped   -f $Pester.SkippedCount),
            ($Script:ReportStrings.StepsPending   -f $Pester.PendingCount),
            ($Script:ReportStrings.StepsPassed    -f $Pester.PassedCount)
        )

        $StepSummaryData = foreach ($count in $StepSummaryCounts) {
            $null = $count -match '^(?<StepCount>\d+) (?<Result>failed|undefined|skipped|pending|passed|steps? \()'
            switch ($Matches.Result) {
                failed    { $Foreground    = $Failure;                       break }
                undefined { $Foreground    = $Undefined;                     break }
                skipped   { $Foreground    = $Skipped;                       break }
                pending   { $Foreground    = $Pending;                       break }
                passed    { $Foreground    = $Passed;                        break }
                default   { $Foreground    = $Script:ReportTheme.Foreground; break }
            }

            if ($Matches.StepCount -gt 0) {
                [PSCustomObject]@{ Foreground = $Foreground; Text = $count }
            }
        }

        for ($i = 0; $i -lt $StepSummaryData.Length; $i++) {
            $SummaryData = $StepSummaryData[$i]
            if ($i -eq $StepSummaryData.Length - 1) {
                & $WriteHost ($SummaryData.Text -replace ', ') -Foreground $SummaryData.Foreground -NoNewLine
                & $WriteHost ')' -Foreground $Script:ReportTheme.Foreground
            }
            else {
                & $WriteHost $SummaryData.Text -Foreground $SummaryData.Foreground -NoNewLine
                if ($i) {
                    & $WriteHost ', ' -Foreground $Script:ReportTheme.Foreground -NoNewLine
                }
            }
        }

        & $WriteHost (
            $Script:ReportStrings.Timing -f (
                $Pester.TestResult |
                    Select-Object -ExpandProperty Time |
                    ForEach-Object -Begin {
                        $TotalTime = [TimeSpan]::Zero
                    } -Process {
                        $TotalTime += $_
                    } -End { $TotalTime }
            )
        ) -ForegroundColor $Script:ReportTheme.Foreground
        & $WriteHost

        # TODO: Can we create a method that would auto-generate the Step Definition script blocks to the console for undefined steps?
        # You can implement step definitions for undefined steps with these snippets:
        #
        # Given "the input '(.*?)'" {
        # param($arg1)
        # Set-TestPending
        # }
    }
}

# SIG # Begin signature block
# MIIZbgYJKoZIhvcNAQcCoIIZXzCCGVsCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU0TiLukJlIIko1sjhuQoOPp01
# U5+gghR8MIIE/jCCA+agAwIBAgIQDUJK4L46iP9gQCHOFADw3TANBgkqhkiG9w0B
# AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz
# c3VyZWQgSUQgVGltZXN0YW1waW5nIENBMB4XDTIxMDEwMTAwMDAwMFoXDTMxMDEw
# NjAwMDAwMFowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMTCCASIwDQYJKoZIhvcN
# AQEBBQADggEPADCCAQoCggEBAMLmYYRnxYr1DQikRcpja1HXOhFCvQp1dU2UtAxQ
# tSYQ/h3Ib5FrDJbnGlxI70Tlv5thzRWRYlq4/2cLnGP9NmqB+in43Stwhd4CGPN4
# bbx9+cdtCT2+anaH6Yq9+IRdHnbJ5MZ2djpT0dHTWjaPxqPhLxs6t2HWc+xObTOK
# fF1FLUuxUOZBOjdWhtyTI433UCXoZObd048vV7WHIOsOjizVI9r0TXhG4wODMSlK
# XAwxikqMiMX3MFr5FK8VX2xDSQn9JiNT9o1j6BqrW7EdMMKbaYK02/xWVLwfoYer
# vnpbCiAvSwnJlaeNsvrWY4tOpXIc7p96AXP4Gdb+DUmEvQECAwEAAaOCAbgwggG0
# MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsG
# AQUFBwMIMEEGA1UdIAQ6MDgwNgYJYIZIAYb9bAcBMCkwJwYIKwYBBQUHAgEWG2h0
# dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAfBgNVHSMEGDAWgBT0tuEgHf4prtLk
# YaWyoiWyyBc1bjAdBgNVHQ4EFgQUNkSGjqS6sGa+vCgtHUQ23eNqerwwcQYDVR0f
# BGowaDAyoDCgLoYsaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJl
# ZC10cy5jcmwwMqAwoC6GLGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFz
# c3VyZWQtdHMuY3JsMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUHMAGGGGh0dHA6
# Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDovL2NhY2VydHMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRFRpbWVzdGFtcGluZ0NB
# LmNydDANBgkqhkiG9w0BAQsFAAOCAQEASBzctemaI7znGucgDo5nRv1CclF0CiNH
# o6uS0iXEcFm+FKDlJ4GlTRQVGQd58NEEw4bZO73+RAJmTe1ppA/2uHDPYuj1UUp4
# eTZ6J7fz51Kfk6ftQ55757TdQSKJ+4eiRgNO/PT+t2R3Y18jUmmDgvoaU+2QzI2h
# F3MN9PNlOXBL85zWenvaDLw9MtAby/Vh/HUIAHa8gQ74wOFcz8QRcucbZEnYIpp1
# FUL1LTI4gdr0YKK6tFL7XOBhJCVPst/JKahzQ1HavWPWH1ub9y4bTxMd90oNcX6X
# t/Q/hOvB46NJofrOp79Wz7pZdmGJX36ntI5nePk2mOHLKNpbh6aKLzCCBQ0wggP1
# oAMCAQICEAPBBFtdmcD9x4iXgGyKU+cwDQYJKoZIhvcNAQELBQAwcjELMAkGA1UE
# BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj
# ZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUg
# U2lnbmluZyBDQTAeFw0yMTAxMjgwMDAwMDBaFw0yNDAxMzEyMzU5NTlaMEsxCzAJ
# BgNVBAYTAkNaMQ4wDAYDVQQHEwVQcmFoYTEVMBMGA1UECgwMSmFrdWIgSmFyZcWh
# MRUwEwYDVQQDDAxKYWt1YiBKYXJlxaEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
# ggEKAoIBAQC154nkzSQ95K1yCflVL3iFQhzw/25ht4o506hK7ATFgvP71ZI+YHce
# gvVoMtaMhJp2/EzXsgxDF7EZBR4Hl9vNbIpLAFSVCK4GBD0DxNCDFrJTPtNohgsA
# STNMcK6t0iunh7MEkaYt1yPgiISA1vcQUMKi51WSUxeWnsUNTkJDZkyM61fETbhy
# CI66xLItaf3OWdyjiOFPq2n8yx+eg1w7GCC/eNYVAjzqtSmiE/xv6Qoj7z9qFyS1
# pAO4cxDRLAD9IcCiYmHOJVgsho3/u4QNNm72ghz7iiRAO5lDoBcZIiLS5RKxJwMG
# nnYbIiAuISZmv4PtrkcSu81Lzmtu81idAgMBAAGjggHEMIIBwDAfBgNVHSMEGDAW
# gBRaxLl7KgqjpepxA8Bg+S32ZXUOWDAdBgNVHQ4EFgQUF2nEZEX1uTrPSD3h5VSJ
# 8g9ef20wDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGA1Ud
# HwRwMG4wNaAzoDGGL2h0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3Vy
# ZWQtY3MtZzEuY3JsMDWgM6Axhi9odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hh
# Mi1hc3N1cmVkLWNzLWcxLmNybDBLBgNVHSAERDBCMDYGCWCGSAGG/WwDATApMCcG
# CCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQB
# MIGEBggrBgEFBQcBAQR4MHYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj
# ZXJ0LmNvbTBOBggrBgEFBQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t
# L0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB
# /wQCMAAwDQYJKoZIhvcNAQELBQADggEBANuuPZ7LhU/v1GDprUhYch3/fov3sIxp
# wshFvufWbGxUYzxEVQSsZLdFVUzAdsCz3fKInY2ihCwqIoU5vbwKFvV1zvizat/r
# aNn5aa8H34NjEXPHQiyNykOk9CdFgk+zZn+YpeyzBMAEvQR4uB4eDv1USWkwdXPB
# VVZcjM0xEsx9H/ZZRSEGS0x3ue+shvZdPRzoWcuiK8hNcbFZr15hMGi4s0F9IxTZ
# QzoSpNJsBA/vMmkbp2SWeENn49BNx8q760e+ELMfuSBltKs8S2hB9TLrpko3nIvp
# l1323zyR6ZpWK1/FHbGkHRsSJKvOOBdlSL08+KM2kNzXez88eUae+1YwggUwMIIE
# GKADAgECAhAECRgbX9W7ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNV
# BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp
# Y2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAe
# Fw0xMzEwMjIxMjAwMDBaFw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUw
# EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
# MTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcg
# Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9ML
# MUkZz9D7RZmxOttE9X/lqJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWs
# DnkoOn7p0WfTxvspJ8fTeyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeK
# iUXULaGj6YgsIJWuHEqHCN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5Tsx
# HM/q8grkV7tKtel05iv+bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sI
# ZD5SlsHyDxL0xY4PwaLoLFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/Rnf
# JZPRAgMBAAGjggHNMIIByTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQE
# AwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYB
# BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0
# cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNydDCBgQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29t
# L0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAE
# SDBGMDgGCmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGln
# aWNlcnQuY29tL0NQUzAKBghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPA
# YPkt9mV1DlgwHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZI
# hvcNAQELBQADggEBAD7sDVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0P
# xK+L/e8q3yBVN7Dh9tGSdQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK
# 95xGTlz/kLEbBw6RFfu6r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6
# aGivm6dcIFzZcbEMj7uo+MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lF
# luhZHen6dGRrsutmQ9qzsIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmC
# SfdibqFT+hKUGIUukpHqaGxEMrJmoecYpJpkUe8wggUxMIIEGaADAgECAhAKoSXW
# 1jIbfkHkBdo2l8IVMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYD
# VQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAi
# BgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xNjAxMDcxMjAw
# MDBaFw0zMTAxMDcxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp
# Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERp
# Z2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwggEiMA0GCSqG
# SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC90DLuS82Pf92puoKZxTlUKFe2I0rEDgdF
# M1EQfdD5fU1ofue2oPSNs4jkl79jIZCYvxO8V9PD4X4I1moUADj3Lh477sym9jJZ
# /l9lP+Cb6+NGRwYaVX4LJ37AovWg4N4iPw7/fpX786O6Ij4YrBHk8JkDbTuFfAnT
# 7l3ImgtU46gJcWvgzyIQD3XPcXJOCq3fQDpct1HhoXkUxk0kIzBdvOw8YGqsLwfM
# /fDqR9mIUF79Zm5WYScpiYRR5oLnRlD9lCosp+R1PrqYD4R/nzEU1q3V8mTLex4F
# 0IQZchfxFwbvPc3WTe8GQv2iUypPhR3EHTyvz9qsEPXdrKzpVv+TAgMBAAGjggHO
# MIIByjAdBgNVHQ4EFgQU9LbhIB3+Ka7S5GGlsqIlssgXNW4wHwYDVR0jBBgwFoAU
# Reuir/SSy4IxLVGLp6chnfNtyA8wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8B
# Af8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgweQYIKwYBBQUHAQEEbTBrMCQG
# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
# b3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9j
# cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwUAYD
# VR0gBEkwRzA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
# LmRpZ2ljZXJ0LmNvbS9DUFMwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4IB
# AQBxlRLpUYdWac3v3dp8qmN6s3jPBjdAhO9LhL/KzwMC/cWnww4gQiyvd/MrHwwh
# Wiq3BTQdaq6Z+CeiZr8JqmDfdqQ6kw/4stHYfBli6F6CJR7Euhx7LCHi1lssFDVD
# BGiy23UC4HLHmNY8ZOUfSBAYX4k4YU1iRiSHY4yRUiyvKYnleB/WCxSlgNcSR3Cz
# ddWThZN+tpJn+1Nhiaj1a5bA9FhpDXzIAbG5KHW3mWOFIoxhynmUfln8jA/jb7UB
# JrZspe6HUSHkWGCbugwtK22ixH67xCUrRwIIfEmuE7bhfEJCKMYYVs9BNLZmXbZ0
# e/VWMyIvIjayS6JKldj1po5SMYIEXDCCBFgCAQEwgYYwcjELMAkGA1UEBhMCVVMx
# FTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNv
# bTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmlu
# ZyBDQQIQA8EEW12ZwP3HiJeAbIpT5zAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIB
# DDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEE
# AYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUvHazV9H9cfnx
# LzcNTNQi3C7qbpgwDQYJKoZIhvcNAQEBBQAEggEAixdVoWaWBAYHSdmByBdJGAR3
# OnO3Opj6n03aD4eQTqIYbIlCg4SwYfr8oIeNNajPsUdyjIKeCwMVf48Fc9lQAyDZ
# xBds1nOjW7iIX0FsyvIUDNQYlM8IuneQMajp/f9IVHq3B++AjWHB5Jyjay2z9qYF
# mmw5Len1PrjKtvl7o+uwsR319Nqw51yzbcBZjenr5l2JqmOBEg5BmKBR2l59g36L
# yphTrB+0x1273wwGSvnShk+JEOf6ytwfYYSbHT+ar3HfLop7RStDHOnXcZE0qnSC
# 0G1roAf23HdCqbTdHyKEm34g2EDoUUDi+pF629hqgBztdYHSumLwcovZfDZlAqGC
# AjAwggIsBgkqhkiG9w0BCQYxggIdMIICGQIBATCBhjByMQswCQYDVQQGEwJVUzEV
# MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t
# MTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5n
# IENBAhANQkrgvjqI/2BAIc4UAPDdMA0GCWCGSAFlAwQCAQUAoGkwGAYJKoZIhvcN
# AQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjEwNTI5MTIyODAzWjAv
# BgkqhkiG9w0BCQQxIgQgLPy62Mb5MHMKGC8C9m9f6PpA0xZKrQqM8rdQ3CMgDCow
# DQYJKoZIhvcNAQEBBQAEggEAU6S0rFPxoEXyoWydI7a8eWOXTA3TOvW5O23chUy0
# gbxEJggbqhrsJ9aTjy3EFMckhYPNSXJ/T+OKvGjvoo2h34ZxHbgbZUg+S66LfMmR
# uYIyMtw3GQcH1IbjQA2sbNK/EdTdysDb7SCgwS3lrcOJuENWPAwqBLe9+5SdUIUT
# cAD5JWBIfw0YSUSnvjnfCto+fkbczZsX1KfvzwY+++a+E66dpLMXpcuH1n5k0NQX
# qZ5SmeBwyMGoPV/Hv2qKK3TGmARY3piiLYjvWyfJRe2txQ6JnIcBeJP91Lh7Dbkw
# ouauMyaBOq/OQ+mGquzqZh/N4bnKEIuGRlV5g7HrNRZZKQ==
# SIG # End signature block