Skip to content

Commit 18c2758

Browse files
nohwndCopilot
andauthored
Improve Should-BeString to show string diff with arrow marker (#2731)
* Improve Should-BeString to show difference index and arrow marker When strings differ, show the difference index, expanded special characters (e.g. \n, \r, \t), and an arrow pointing to the first difference — matching the style of Should -Be. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Replace wildcard test assertions with exact here-string validation Verify arrow position precisely using Verify-Equal with here-strings instead of Verify-Like wildcards. Add test for mid-string difference showing dashes before the arrow marker. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix cross-platform newline in diff message Use explicit LF in Get-StringDifferenceMessage and normalize expected values in tests with -replace to match on all platforms. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4b82fb2 commit 18c2758

2 files changed

Lines changed: 114 additions & 2 deletions

File tree

src/functions/assert/String/Should-BeString.ps1

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,75 @@ function Should-BeString {
9696

9797
$stringsAreEqual = Test-StringEqual -Expected $Expected -Actual $Actual -CaseSensitive:$CaseSensitive -IgnoreWhitespace:$IgnoreWhiteSpace -TrimWhitespace:$TrimWhitespace
9898
if (-not ($stringsAreEqual)) {
99-
$Message = Get-AssertionMessage -Expected $Expected -Actual $Actual -Because $Because -DefaultMessage "Expected <expectedType> <expected>, but got <actualType> <actual>."
99+
if ($Actual -is [string]) {
100+
$Message = Get-StringDifferenceMessage -Expected $Expected -Actual $Actual -CaseSensitive:$CaseSensitive -Because $Because
101+
}
102+
else {
103+
$Message = Get-AssertionMessage -Expected $Expected -Actual $Actual -Because $Because -DefaultMessage "Expected <expectedType> <expected>, but got <actualType> <actual>."
104+
}
100105
Invoke-AssertionFailed -Message $Message -CallerCmdlet $PSCmdlet
101106
}
102107
Set-AssertionPassResult
103108
}
109+
110+
function Get-StringDifferenceMessage {
111+
param (
112+
[Parameter(Mandatory)]
113+
[AllowEmptyString()]
114+
[string] $Expected,
115+
[Parameter(Mandatory)]
116+
[AllowEmptyString()]
117+
[string] $Actual,
118+
[switch] $CaseSensitive,
119+
[string] $Because
120+
)
121+
122+
$maxLength = [Math]::Max($Expected.Length, $Actual.Length)
123+
124+
$differenceIndex = $null
125+
for ($i = 0; $i -lt $maxLength -and ($null -eq $differenceIndex); ++$i) {
126+
if ($CaseSensitive) {
127+
if ($Expected[$i] -cne $Actual[$i]) { $differenceIndex = $i }
128+
}
129+
else {
130+
if ($Expected[$i] -ne $Actual[$i]) { $differenceIndex = $i }
131+
}
132+
}
133+
134+
$because = if ($Because) { " because $Because," } else { "" }
135+
136+
$lines = @(
137+
"Expected strings to be the same,$because but they were different."
138+
)
139+
140+
if ($Expected.Length -ne $Actual.Length) {
141+
$lines += "Expected length: $($Expected.Length)"
142+
$lines += "Actual length: $($Actual.Length)"
143+
}
144+
else {
145+
$lines += "String lengths are both $($Expected.Length)."
146+
}
147+
$lines += "Strings differ at index $differenceIndex."
148+
149+
$expectedExpanded = Expand-SpecialCharacters -InputObject $Expected
150+
$actualExpanded = Expand-SpecialCharacters -InputObject $Actual
151+
152+
# Recompute difference index on expanded strings
153+
$maxLength = [Math]::Max($expectedExpanded.Length, $actualExpanded.Length)
154+
$expandedDiffIndex = $null
155+
for ($i = 0; $i -lt $maxLength -and ($null -eq $expandedDiffIndex); ++$i) {
156+
if ($CaseSensitive) {
157+
if ($expectedExpanded[$i] -cne $actualExpanded[$i]) { $expandedDiffIndex = $i }
158+
}
159+
else {
160+
if ($expectedExpanded[$i] -ne $actualExpanded[$i]) { $expandedDiffIndex = $i }
161+
}
162+
}
163+
164+
$prefix = "Expected: '"
165+
$lines += "$prefix$expectedExpanded'"
166+
$lines += "But was: '$actualExpanded'"
167+
$lines += (' ' * ($prefix.Length - 1)) + ('-' * $expandedDiffIndex) + '^'
168+
169+
$lines -join "`n"
170+
}

tst/functions/assert/String/Should-BeString.Tests.ps1

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,51 @@ Describe "Should-BeString" {
118118

119119
It "Throws with default message when test fails" {
120120
$err = { Should-BeString -Expected "abc" -Actual "bde" } | Verify-AssertionFailed
121-
$err.Exception.Message | Verify-Equal "Expected [string] 'abc', but got [string] 'bde'."
121+
$err.Exception.Message | Verify-Equal (@'
122+
Expected strings to be the same, but they were different.
123+
String lengths are both 3.
124+
Strings differ at index 0.
125+
Expected: 'abc'
126+
But was: 'bde'
127+
^
128+
'@ -replace "`r`n", "`n")
129+
}
130+
131+
It "Shows arrow at correct position for case-sensitive difference" {
132+
$err = { "hello world" | Should-BeString "Hello World" -CaseSensitive } | Verify-AssertionFailed
133+
$err.Exception.Message | Verify-Equal (@'
134+
Expected strings to be the same, but they were different.
135+
String lengths are both 11.
136+
Strings differ at index 0.
137+
Expected: 'Hello World'
138+
But was: 'hello world'
139+
^
140+
'@ -replace "`r`n", "`n")
141+
}
142+
143+
It "Shows arrow with dashes for mid-string difference" {
144+
$err = { "abc" | Should-BeString "abcdef" } | Verify-AssertionFailed
145+
$err.Exception.Message | Verify-Equal (@'
146+
Expected strings to be the same, but they were different.
147+
Expected length: 6
148+
Actual length: 3
149+
Strings differ at index 3.
150+
Expected: 'abcdef'
151+
But was: 'abc'
152+
---^
153+
'@ -replace "`r`n", "`n")
154+
}
155+
156+
It "Shows expanded whitespace characters in diff" {
157+
$err = { "abc`ndef" | Should-BeString "abc`r`ndef" } | Verify-AssertionFailed
158+
$err.Exception.Message | Verify-Equal (@'
159+
Expected strings to be the same, but they were different.
160+
Expected length: 8
161+
Actual length: 7
162+
Strings differ at index 3.
163+
Expected: 'abc\r\ndef'
164+
But was: 'abc\ndef'
165+
----^
166+
'@ -replace "`r`n", "`n")
122167
}
123168
}

0 commit comments

Comments
 (0)