pwsh-handy-helpers.Tests.ps1
if (Get-Module -Name 'pwsh-handy-helpers') { Remove-Module -Name 'pwsh-handy-helpers' } Import-Module "${PSScriptRoot}\pwsh-handy-helpers.psm1" -Force Describe "Handy Helpers Module" { Context "meta validation" { It "should import exports" { (Get-Module -Name pwsh-handy-helpers).ExportedFunctions.Count | Should -Be 51 } It "should import aliases" { (Get-Module -Name pwsh-handy-helpers).ExportedAliases.Count | Should -Be 21 } } } Describe "ConvertTo-PowershellSyntax" { It "can act as pass-thru for normal strings" { $Expected = "normal string with not mustache templates" $Expected | ConvertTo-PowershellSyntax | Should -Be $Expected } It "can convert strings with single mustache template" { $InputString = 'Hello {{ world }}' $Expected = 'Hello $($Data.world)' $InputString | ConvertTo-PowershellSyntax | Should -Be $Expected } It "can convert strings with multiple mustache templates without regard to spaces" { $Expected = '$($Data.hello) $($Data.world)' '{{ hello }} {{ world }}' | ConvertTo-PowershellSyntax | Should -Be $Expected '{{ hello }} {{ world}}' | ConvertTo-PowershellSyntax | Should -Be $Expected '{{hello }} {{world}}' | ConvertTo-PowershellSyntax | Should -Be $Expected } It "will not convert mustache helper templates" { $Expected = 'The sky is {{#blue blue }}' $Expected | ConvertTo-PowershellSyntax | Should -Be $Expected $Expected = '{{#red greet }}, my name $($Data.foo) $($Data.foo) is $($Data.name)' '{{#red greet }}, my name {{foo }} {{foo}} is {{ name }}' | ConvertTo-PowershellSyntax | Should -Be $Expected '{{#red Red}} {{#blue Blue}}' | ConvertTo-PowershellSyntax | Should -Be '{{#red Red}} {{#blue Blue}}' } It "supports template variables within mustache helper templates" { '{{#green Hello}} {{#red {{ name }}}}' | ConvertTo-PowershellSyntax | Should -Be '{{#green Hello}} {{#red $($Data.name)}}' '{{#green Hello}} {{#red {{ name }} }}' | ConvertTo-PowershellSyntax | Should -Be '{{#green Hello}} {{#red $($Data.name) }}' '{{#green Hello}} {{#red {{ foo }}{{ bar }}}}' | ConvertTo-PowershellSyntax | Should -Be '{{#green Hello}} {{#red $($Data.foo)$($Data.bar)}}' '{{#green Hello}} {{#red {{foo}}{{bar}}}}' | ConvertTo-PowershellSyntax | Should -Be '{{#green Hello}} {{#red $($Data.foo)$($Data.bar)}}' '{{#green Hello}} {{#red {{ a }} b {{ c }} }}' | ConvertTo-PowershellSyntax | Should -Be '{{#green Hello}} {{#red $($Data.a) b $($Data.c) }}' '{{#green Hello}} {{#red {{a}}b{{c}}}}' | ConvertTo-PowershellSyntax | Should -Be '{{#green Hello}} {{#red $($Data.a)b$($Data.c)}}' '{{#green Hello}} {{#red {{ a }} b {{ c }} d}}' | ConvertTo-PowershellSyntax | Should -Be '{{#green Hello}} {{#red $($Data.a) b $($Data.c) d}}' '{{#green Hello}} {{#red {{a}}b{{c}}d}}' | ConvertTo-PowershellSyntax | Should -Be '{{#green Hello}} {{#red $($Data.a)b$($Data.c)d}}' } } Describe "Find-Duplicates" { It "can identify duplicate files" { $Same = "these files have identical content" $Same | Out-File "TestDrive:\foo" "unique" | Out-File "TestDrive:\bar" $Same | Out-File "TestDrive:\baz" mkdir "TestDrive:\sub" $Same | Out-File "TestDrive:\sub\bam" "also unique" | Out-File "TestDrive:\sub\bat" Find-Duplicate "TestDrive:\" | ForEach-Object { Get-Item $_.Path } | Select-Object -ExpandProperty Name | Sort-Object | Should -Be "bam","baz","foo" } } Describe "Find-FirstIndex" { It "can determine index of first item that satisfies default predicate" { Find-FirstIndex -Values $false,$true,$false | Should -Be 1 ,($false, $true, $false) | Find-FirstIndex | Should -Be 1 } It "can determine index of first item that satisfies passed predicate" { $Arr = 1,1,1,1,2,1,1 $Predicate = { $args[0] -eq 2 } Find-FirstIndex -Values $Arr | Should -Be $null Find-FirstIndex -Values $Arr -Predicate $Predicate | Should -Be 4 ,$Arr | Find-FirstIndex -Predicate $Predicate | Should -Be 4 } } Describe "Invoke-InsertString" { It "can insert string into a string at a given index" { Invoke-InsertString -Value "C" -To "ABDE" -At 2 | Should -Be "ABCDE" "C" | Invoke-InsertString -To "ABDE" -At 2 | Should -Be "ABCDE" "234" | Invoke-InsertString -To "15" -At 1 | Should -Be "12345" "bar" | Invoke-InsertString -To "foo" -At 3 | Should -Be "foobar" "bar" | Invoke-InsertString -To "foo" -At 4 | Should -Be "foo" } } # Describe "Invoke-ListenTo" { # AfterEach { # "TestEvent" | Invoke-StopListen # } # It "can listen to custom events and trigger actions" { # function Test-Callback {} # $EventName = "TestEvent" # $Times = 5 # Mock Test-Callback {} # { Test-Callback } | Invoke-ListenTo $EventName # 1..$Times | ForEach-Object { Invoke-FireEvent $EventName -Data "test" } # Assert-MockCalled Test-Callback -Times $Times # } # It "can listen to custom events and trigger one-time action" { # function Test-Callback {} # $EventName = "TestEvent" # Mock Test-Callback {} # { Test-Callback } | Invoke-ListenTo $EventName -Once # 1..10 | ForEach-Object { Invoke-FireEvent $EventName -Data "test" } # Assert-MockCalled Test-Callback -Times 1 # } # } Describe "Invoke-Once" { It "will return a function that will only be executed once" { function Test-Callback {} $Function:test = Invoke-Once { Test-Callback } Mock Test-Callback {} 1..10 | ForEach-Object { test } Assert-MockCalled Test-Callback -Times 1 } It "will return a function that will only be executed a certain number of times" { function Test-Callback {} $Times = 5 $Function:test = Invoke-Once -Times $Times { Test-Callback } Mock Test-Callback {} 1..10 | ForEach-Object { test } Assert-MockCalled Test-Callback -Times $Times } } Describe "Invoke-Reduce" { It "can accept strings and integers as initial values" { $Add = { Param($a, $b) $a + $b } 1,2,3,4,5 | Invoke-Reduce -Callback $Add -InitialValue 0 | Should -Be 15 "a","b","c" | Invoke-Reduce -Callback $Add -InitialValue "" | Should -Be "abc" "a","b","c" | Invoke-Reduce -InitialValue "initial value" | Should -Be "initial value" } It "can accept boolean values" { $Every = { Param($a, $b) $a -and $b } $Some = { Param($a, $b) $a -or $b } $AllTrue = $true,$true,$true $OneFalse = $true,$false,$true $AllTrue | Invoke-Reduce -Callback $Every -InitialValue $true | Should -Be $true $OneFalse | Invoke-Reduce -Callback $Some -InitialValue $true | Should -Be $true $AllTrue | Invoke-Reduce -Callback $Some -InitialValue $true | Should -Be $true $OneFalse | Invoke-Reduce -Callback $Every -InitialValue $true | Should -Be $false } It "can accept objects as initial values" { $a = @{ name = "a"; value = 1 } $b = @{ name = "b"; value = 2 } $c = @{ name = "c"; value = 3 } $Callback = { Param($Acc, $Item) $Acc[$Item.Name] = $Item.Value } # with inline scriptblock $Result = $a,$b,$c | Invoke-Reduce -Callback { Param($Acc, $Item) $Acc[$Item.Name] = $Item.Value } $Result.Keys | Sort-Object | Should -Be "a","b","c" $Result.Values | Sort-Object | Should -Be 1,2,3 # with scriptblock variable $Result = $a,$b,$c | Invoke-Reduce -Callback $Callback $Result.Keys | Sort-Object | Should -Be "a","b","c" $Result.Values | Sort-Object | Should -Be 1,2,3 } It "can combine FileInfo objects" { $Result = Get-ChildItem -File | Invoke-Reduce -FileInfo $Result.Keys | Should -Contain "pwsh-handy-helpers.psm1" $Result.Keys | Should -Contain "pwsh-handy-helpers.psd1" $Result.Keys | Should -Contain "pwsh-handy-helpers.Tests.ps1" $Result.Values | ForEach-Object { $_ | Should -BeOfType [Long] } } } Describe "Invoke-Speak (say)" { It "can passthru text without speaking" { $Text = "this should not be heard" Invoke-Speak $Text -Silent | Should -Be $null Invoke-Speak $Text -Silent -Output text | Should -Be $Text } It "can output SSML" { $Text = "this should not be heard either" Invoke-Speak $Text -Silent -Output ssml | Should -match "<p>$Text</p>" } It "can output SSML with custom rate" { $Text = "this should not be heard either" $Rate = 10 Invoke-Speak $Text -Silent -Output ssml -Rate $Rate | Should -match "<p>$Text</p>" Invoke-Speak $Text -Silent -Output ssml -Rate $Rate | Should -match "<prosody rate=`"$Rate`">" } } Describe "Join-StringsWithGrammar" { It "accepts one parameter" { Join-StringsWithGrammar "one" | Should -Be "one" Join-StringsWithGrammar -Items "one" | Should -Be "one" "one" | Join-StringsWithGrammar | Should -Be "one" Join-StringsWithGrammar @("one") | Should -Be "one" } It "accepts two parameter" { Join-StringsWithGrammar "one","two" | Should -Be "one and two" Join-StringsWithGrammar -Items "one","two" | Should -Be "one and two" "one","two" | Join-StringsWithGrammar | Should -Be "one and two" Join-StringsWithGrammar @("one", "two") | Should -Be "one and two" } It "accepts three or more parameters" { Join-StringsWithGrammar "one","two","three" | Should -Be "one, two, and three" Join-StringsWithGrammar -Items "one","two","three" | Should -Be "one, two, and three" Join-StringsWithGrammar "one","two","three","four" | Should -be "one, two, three, and four" "one","two","three" | Join-StringsWithGrammar | Should -Be "one, two, and three" "one","two","three","four" | Join-StringsWithGrammar | Should -be "one, two, three, and four" Join-StringsWithGrammar @("one", "two", "three") | Should -Be "one, two, and three" Join-StringsWithGrammar @("one", "two", "three", "four") | Should -Be "one, two, three, and four" } } Describe "New-File (touch)" { AfterAll { Remove-Item -Path .\SomeFile } It "can create a file" { $Content = "testing" ".\SomeFile" | Should -not -Exist New-File SomeFile Write-Output $Content >> .\SomeFile ".\SomeFile" | Should -FileContentMatch $Content } } Describe "New-Template" { Context "when passed an empty object" { $script:Expected = "<div>Hello </div>" It "can return function that accepts positional parameter" { $function:render = New-Template '<div>Hello {{ name }}</div>' render @{} | Should -Be $Expected } It "can return function when instantiated as function variable" { $function:render = New-Template -Template '<div>Hello {{ name }}</div>' render @{} | Should -Be $Expected } It "can return function when instantiated as normal variable" { $renderVariable = New-Template -Template '<div>Hello {{ name }}</div>' & $renderVariable @{} | Should -Be $Expected } It "can support default values" { $renderVariable = New-Template -Template '<div>Hello {{ name }}</div>' -DefaultValues @{ name = "Default" } & $renderVariable | Should -Be "<div>Hello Default</div>" & $renderVariable @{ name = "Not Default" } | Should -Be "<div>Hello Not Default</div>" } } It "can return a string when passed -Data paramter" { 'Hello {{ name }}' | New-Template -Data @{ name = "World" } | Should -Be "Hello World" '{{#green Hello}} {{ name }}' | New-Template -Data @{ name = "World" } | Should -Be "{{#green Hello}} World" } It "can create function from template string using mustache notation" { $Expected = "<div>Hello World!</div>" $function:render = New-Template '<div>Hello {{ name }}!</div>' render @{ name = "World" } | Should -Be $Expected @{ name = "World" } | render | Should -Be $Expected } It "can create function from template string using Powershell syntax" { $Expected = "<div>Hello World!</div>" $function:render = New-Template '<div>Hello $($Data.name)!</div>' render @{ name = "World" } | Should -Be $Expected @{ name = "World" } | render | Should -Be $Expected } It "can be nested within other templates" { $Expected = "<section> <h1>Title</h1> <div>Hello World!</div> </section>" $div = New-Template -Template '<div>{{ text }}</div>' $section = New-Template "<section> <h1>{{ title }}</h1> $(& $div @{text = "Hello World!"}) </section>" & $section @{ title = "Title" } | Should -Be $Expected } It "can be nested within other templates (with Powershell syntax)" { $Expected = "<section> <h1>Title</h1> <div>Hello World!</div> </section>" $div = New-Template -Template '<div>{{ text }}</div>' $section = New-Template "<section> <h1>`$(`$Data.title)</h1> $(& $div @{text = "Hello World!"}) </section>" & $section @{ title = "Title" } | Should -Be $Expected } It "can return pass-thru function that does no string interpolation" { $Function:render = '{{#green Hello}} {{ name }}' | New-Template render -Data @{ name = "Jason" } | Should -Be '{{#green Hello}} Jason' render -Data @{ name = "Jason" } -PassThru | Should -Be '{{#green Hello}} {{ name }}' } } Describe "Remove-Character" { It "can remove single character from string" { "012345" | Remove-Character -At 0 | Should -Be "12345" "012345" | Remove-Character -At 2 | Should -Be "01345" "012345" | Remove-Character -At 5 | Should -Be "01234" } It "will return entire string if out-of-bounds index" { "012345" | Remove-Character -At 10 | Should -Be "012345" } It "can remove the first character of a string" { "XOOOOO" | Remove-Character -First | Should -Be "OOOOO" } It "can remove the last character of a string" { "OOOOOX" | Remove-Character -Last | Should -Be "OOOOO" } It "can remove last character from a string" { "A" | Remove-Character -At 0 | Should -Be "" } } Describe "Remove-DirectoryForce (rf)" { It "can create a file" { New-File SomeFile ".\SomeFile" | Should -Exist Remove-DirectoryForce .\SomeFile ".\SomeFile" | Should -Not -Exist } } # Describe "Test-Admin" { # It "should return false if not Administrator" { # Test-Admin | Should -Be $false # } # } Describe "Test-Empty" { It "should return true for directories with no contents" { "TestDrive:\Foo" | Should -not -Exist mkdir "TestDrive:\Foo" "TestDrive:\Foo" | Should -Exist Test-Empty "TestDrive:\Foo" | Should -Be $true mkdir "TestDrive:\Foo\Bar" mkdir "TestDrive:\Foo\Bar\Baz" Test-Empty "TestDrive:\Foo" | Should -Be $false } } Describe "Test-Equal" { It "can compare numbers" { Test-Equal 0 0 | Should -Be $true Test-Equal 42 42 | Should -Be $true Test-Equal -42 -42 | Should -Be $true Test-Equal 42 43 | Should -Be $false Test-Equal -43 -42 | Should -Be $false Test-Equal 3 "not a number" | Should -Be $false Test-Equal 4.2 4.2 | Should -Be $true Test-Equal 4 4.0 | Should -Be $true Test-Equal 4.1 4.2 | Should -Be $false } It "can compare strings" { Test-Equal "" "" | Should -Be $true Test-Equal "foo" "foo" | Should -Be $true Test-Equal "foo" "bar" | Should -Be $false Test-Equal "foo" 7 | Should -Be $false } It "can compare arrays" { $a = 1,2,3 $b = 1,2,3 $c = 5,6,7 Test-Equal $a $b | Should -Be $true Test-Equal $a $c | Should -Be $false $a = "a","b","c" $b = "a","b","c" $c = "x","y","z" Test-Equal $a $b | Should -Be $true Test-Equal $b $c | Should -Be $false } It "can compare multi-dimensional arrays" { $x = 1,(1,2,3),(4,5,6),7 $y = 1,(1,2,3),(4,5,6),7 $z = (1,2,3),(1,2,3),(1,2,3) Test-Equal $x $y | Should -Be $true Test-Equal $x $z | Should -Be $false Test-Equal $x 1,(1,2,3),(4,5,6),8 | Should -Be $false } It "can compare hashtables" { $A = @{ a = "A"; b = "B"; c = "C" } $B = @{ a = "A"; b = "B"; c = "C" } $C = @{ foo = "bar"; bin = "baz"; } Test-Equal $A $B | Should -Be $true Test-Equal $A $C | Should -Be $false } It "can compare nested hashtables" { $A = @{ a = "A"; b = "B"; c = "C" } $B = @{ a = "A"; b = "B"; c = "C" } $C = @{ foo = "bar"; bin = "baz"; } $M = @{ a = $A; b = $B; c = $C } $N = @{ a = $A; b = $B; c = $C } $O = @{ a = $C; b = $A; c = $B } Test-Equal $M $N | Should -Be $true Test-Equal $M $O | Should -Be $false } It "can compare custom objects" { $A = [PSCustomObject]@{ a = "A"; b = "B"; c = "C" } $B = [PSCustomObject]@{ a = "A"; b = "B"; c = "C" } $C = [PSCustomObject]@{ foo = "bar"; bin = "baz" } Test-Equal $a $b | Should -Be $true Test-Equal $a $c | Should -Be $false } It "can compare nested custom objects" { $A = [PSCustomObject]@{ a = "A"; b = "B"; c = "C" } $B = [PSCustomObject]@{ a = "A"; b = "B"; c = "C" } $C = [PSCustomObject]@{ foo = "bar"; bin = "baz" } $M = [PSCustomObject]@{ a = $A; b = $B; c = $C } $N = [PSCustomObject]@{ a = $A; b = $B; c = $C } $O = [PSCustomObject]@{ a = $C; b = $A; c = $B } Test-Equal $M $N | Should -Be $true Test-Equal $M $O | Should -Be $false } It "can compare other types" { Test-Equal $true $true | Should -Be $true Test-Equal $false $false | Should -Be $true Test-Equal $true $false | Should -Be $false Test-Equal $null $null | Should -Be $true } } Describe "Test-Installed" { It "should return true if passed module is installed" { Test-Installed Pester | Should -Be $true Test-Installed NotInstalledModule | Should -Be $false } } Describe "Write-Repeat" { It "can create string of repeated characters and strings" { Write-Repeat "O" | Should -Be "O" Write-Repeat "O" -Times 0 | Should -Be "" Write-Repeat "O" -Times 3 | Should -Be "OOO" Write-Repeat "" -Times 42 | Should -Be "" } } |