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 |