src/formatters.ps1
using namespace System.Collections.Generic [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")] Param() $ErrorActionPreference = "Stop" ."$PSScriptRoot\types.ps1" function writePending($timestamp, $description) { $symbol = " " $color = "Yellow" $message = "$timestamp [ $symbol ] $description" Write-Host $message -ForegroundColor $color -NoNewline } function writeSuccess($timestamp, $description, $clearString) { $symbol = [char]8730 $color = "Green" $message = "$timestamp [ $symbol ] $description" Write-Host "`r$clearString" -NoNewline Write-Host "`r$message" -ForegroundColor $color } function writeFail($timestamp, $description, $clearString) { $symbol = "X" $color = "Red" $message = "$timestamp [ $symbol ] $description" Write-Host "`r$clearString" -NoNewline Write-Host "`n$message`n" -ForegroundColor $color exit -1 } $fsm = @{ "Test Test Start $false" = { writePending @args @{ "Test Test Stop $true" = { writeSuccess @args $fsm } "Test Test Stop $false" = { writeFail @args } } } "Set Set Start $false" = { writePending @args @{ "Set Set Stop $false" = { writeSuccess @args $fsm } } } "TestSet Test Start $false" = { writePending @args @{ "TestSet Test Stop $true" = { writeSuccess @args $fsm } "TestSet Test Stop $false" = { @{ "TestSet Set Start $false" = { @{ "TestSet Set Stop $false" = { @{ "TestSet Validate Start $false" = { @{ "TestSet Validate Stop $true" = { writeSuccess @args $fsm } "TestSet Validate Stop $false" = { writeFail @args } } } } } } } } } } } } <# .SYNOPSIS Formats Requirement log events as a live-updating checklist .NOTES Uses Write-Host #> function Format-Checklist { [CmdletBinding()] Param( # Logged Requirement lifecycle events [Parameter(Mandatory, ValueFromPipeline)] [Alias("Event")] [RequirementEvent[]]$RequirementEvent ) begin { $previousRequirement = $null $nextFsm = $fsm } process { $requirement = $_.Requirement # build state vector $requirementType = ("Test", "Set" | ? { $requirement.$_ }) -join "" $method = $_.Method $lifecycleState = $_.State $successResult = [bool]$_.Result $stateVector = "$requirementType $method $lifecycleState $successResult" # build transition arguments $timestamp = Get-Date -Date $_.Date -Format "hh:mm:ss" $description = $requirement.Describe $clearString = ' ' * "??:??:?? [ ? ] $($previousRequirement.Describe)".Length $transitionArgs = @($timestamp, $description, $clearString) # transition FSM if (-not $nextFsm[$stateVector]) { throw @" Format-Checklist has reached an unexpected state '$stateVector'. If you are piping the output of Invoke-Requirement directly to this cmdlet, then this is probably a bug in Format-Checklist. "@ } $nextFsm = &$nextFsm[$stateVector] @transitionArgs $previousRequirement = $requirement } } <# .SYNOPSIS Formats every log event with metadata, including a stack of requirement names when using nested Requirements .NOTES Uses Write-Host #> function Format-CallStack { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "")] [CmdletBinding()] Param( # Logged Requirement lifecycle events [Parameter(Mandatory, ValueFromPipeline)] [Alias("Event")] [RequirementEvent[]]$RequirementEvent ) begin { $context = [Stack[string]]::new() } process { $timestamp = Get-Date -Date $_.Date -Format 'hh:mm:ss' $name = $_.Requirement.Name $description = $_.Requirement.Describe $method, $state, $result = $_.Method, $_.State, $_.Result switch ($method) { "Test" { switch ($state) { "Start" { $context.Push($name) $callstack = $context.ToArray() [array]::Reverse($callstack) $serialized = $callstack -join ">" Write-Host "$timestamp [$serialized] BEGIN TEST $description" } "Stop" { $callstack = $context.ToArray() [array]::Reverse($callstack) $serialized = $callstack -join ">" Write-Host "$timestamp [$serialized] END TEST => $result" $context.Pop() | Out-Null } } } "Set" { switch ($state) { "Start" { $context.Push($name) $callstack = $context.ToArray() [array]::Reverse($callstack) $serialized = $callstack -join ">" Write-Host "$timestamp [$serialized] BEGIN SET $description" } "Stop" { $callstack = $context.ToArray() [array]::Reverse($callstack) $serialized = $callstack -join ">" Write-Host "$timestamp [$serialized] END SET" $context.Pop() | Out-Null } } } "Validate" { switch ($state) { "Start" { $context.Push($name) $callstack = $context.ToArray() [array]::Reverse($callstack) $serialized = $callstack -join ">" Write-Host "$timestamp [$serialized] BEGIN TEST $description" } "Stop" { $callstack = $context.ToArray() [array]::Reverse($callstack) $serialized = $callstack -join ">" Write-Host "$timestamp [$serialized] END TEST => $result" $context.Pop() | Out-Null } } } } } } |