From 502ecf6affbb94b75520ef1e65ae1dc4464452c9 Mon Sep 17 00:00:00 2001 From: Jakub Jares Date: Thu, 2 Apr 2026 20:32:29 +0200 Subject: [PATCH] Fix #2555 and #2561: Should-BeNull pipeline and escape chars in error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #2555: Should-BeNull now treats empty pipeline input as $null. When a function returns no output, PowerShell sends @() through the pipeline. Should-BeNull recognizes this as "no output" and treats it as null, matching user expectations. Explicit @() via parameter still fails. #2561: Format-String2 now escapes control characters (NUL, BEL, BS, TAB, FF, CR, LF, ESC) as Unicode control pictures (␀␇␈␉␌␍␊␛) so they are visible in assertion error messages instead of being invisible or corrupting terminal output. This is especially important for ANSI escape sequences. --- src/Format2.ps1 | 13 +++++++++++++ src/functions/assert/General/Should-BeNull.ps1 | 9 +++++++++ .../assert/General/Should-BeNull.Tests.ps1 | 11 +++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/Format2.ps1 b/src/Format2.ps1 index 38348fb0d..22cd6e72c 100644 --- a/src/Format2.ps1 +++ b/src/Format2.ps1 @@ -50,6 +50,19 @@ function Format-String2 ($Value) { return '' } + # Escape control characters so they are visible in error messages. + # Without this, chars like ESC (0x1B used in ANSI sequences) are invisible + # and make error output confusing. See https://github.com/pester/Pester/issues/2561 + $Value = $Value ` + -replace "`0", '␀' ` + -replace "`a", '␇' ` + -replace "`b", '␈' ` + -replace "`t", '␉' ` + -replace "`f", '␌' ` + -replace "`r", '␍' ` + -replace "`n", '␊' ` + -replace "`e", '␛' + "'$Value'" } diff --git a/src/functions/assert/General/Should-BeNull.ps1 b/src/functions/assert/General/Should-BeNull.ps1 index fd7fd2b2f..b7751d3d5 100644 --- a/src/functions/assert/General/Should-BeNull.ps1 +++ b/src/functions/assert/General/Should-BeNull.ps1 @@ -32,6 +32,15 @@ $collectedInput = Collect-Input -ParameterInput $Actual -PipelineInput $local:Input -IsPipelineInput $MyInvocation.ExpectingInput -UnrollInput $Actual = $collectedInput.Actual + + # When a function returns no output and the result is piped to Should-BeNull, + # PowerShell sends an empty array @() through the pipeline. Treat empty pipeline + # input as $null since "no output" is effectively null. + # See https://github.com/pester/Pester/issues/2555 + if ($collectedInput.IsPipelineInput -and $Actual -is [array] -and $Actual.Count -eq 0) { + $Actual = $null + } + if ($null -ne $Actual) { $Message = Get-AssertionMessage -Expected $null -Actual $Actual -Because $Because -DefaultMessage "Expected `$null, but got ''." throw [Pester.Factory]::CreateShouldErrorRecord($Message, $MyInvocation.ScriptName, $MyInvocation.ScriptLineNumber, $MyInvocation.Line.TrimEnd([System.Environment]::NewLine), $true) diff --git a/tst/functions/assert/General/Should-BeNull.Tests.ps1 b/tst/functions/assert/General/Should-BeNull.Tests.ps1 index 7398ae333..37f9a0574 100644 --- a/tst/functions/assert/General/Should-BeNull.Tests.ps1 +++ b/tst/functions/assert/General/Should-BeNull.Tests.ps1 @@ -9,8 +9,15 @@ Describe "Should-BeNull" { { 1 | Should-BeNull } | Verify-AssertionFailed } - It "Given empty array it fails" { - { @() | Should-BeNull } | Verify-AssertionFailed + It "Given empty array piped it passes (void function output is empty array)" { + # When a function returns no output, PowerShell sends @() through the pipeline. + # Should-BeNull treats this as $null since "no output" is effectively null. + # See https://github.com/pester/Pester/issues/2555 + @() | Should-BeNull + } + + It "Given empty array by parameter it fails" { + { Should-BeNull -Actual @() } | Verify-AssertionFailed } It "Returns the given value" {