ClipboardText.Tests.ps1
<# Note: * Make sure this file is saved as *UT8 with BOM*, so that literal non-ASCII characters are interpreted correctly. * When run in WinPSv3+, an attempt is made to run the tests in WinPSv2 too, but note that requires prior installation of v2 support. Also, in PSv2, Pester must be loaded *manually*, via the *full path to its *.psd1 file* (seemingly, v2 doesn't find modules located in \<version>\ subdirs.). For interactive use, the simplest approach is to invoke v2 as follows: powershell.exe -version 2 -Command "Import-Module '$((Get-Module Pester).Path)' #> # Abort on all unhandled errors. $ErrorActionPreference = 'Stop' # PSv2 compatibility: makes sure that $PSScriptRoot reflects this script's folder. if (-not $PSScriptRoot) { $PSScriptRoot = $MyInvocation.MyCommand.Path } # Turn on the latest strict mode, so as to make sure that the ScriptsToProcess # script that runs the prerequisites-check script dot-sourced also works # properly when the caller happens to run with Set-StrictMode -Version Latest # in effect. Set-StrictMode -Version Latest # Make sure that the enclosing module is (re)loaded. # !! In PSv2, this statement causes Pester to run all tests TWICE (?!) Import-Module $PSScriptRoot -Force # Use the platform-appropiate newline. $nl = [Environment]::NewLine # See if we're running on *Windows PowerShell* $isWinPs = $null, 'Desktop' -contains $PSVersionTable.PSEdition Describe StringInputTest { It "Copies and pastes a string correctly." { $string = "Here at $(Get-Date)" Set-ClipboardText $string Get-ClipboardText | Should -BeExactly $string } It "Correctly round-trips non-ASCII characters." { $string = 'Thomas Hübl''s talk about 中文' $string | Set-ClipboardText Get-ClipboardText | Should -BeExactly $string } It "Outputs an array of lines by default" { $lines = 'one', 'two' $string = $lines -join [Environment]::NewLine Set-ClipboardText $string Get-ClipboardText | Should -BeExactly $lines } It "Retrieves a multi-line string as-is with -Raw and doesn't append an extra newline." { "2 lines${nl}with 1 trailing newline${nl}" | Set-ClipboardText Get-ClipboardText -Raw | Should -Not -Match '(\r?\n){2}\z' } } Describe EmptyTests { BeforeEach { 'dummy' | Set-ClipboardText # make sure we start with a nonempty clipboard so we can verify that clearing is effective } It "Not providing input effectively clears the clipboard." { Set-ClipboardText # no input $null -eq (Get-ClipboardText -Raw) | Should -BeTrue } It "Passing the empty string effectively clears the clipboard." { Set-ClipboardText -InputObject '' # Note The PsWinV5+ Set-Clipboard reports a spurious error with '', which we mask behind the scenes. $null -eq (Get-ClipboardText -Raw) | Should -BeTrue } It "Passing `$null effectively clears the clipboard." { Set-ClipboardText -InputObject $null $null -eq (Get-ClipboardText -Raw) | Should -BeTrue } } Describe PassThruTest { It "Set-ClipboardText -PassThru also outputs the text." { $in = "line 1${nl}line 2" $out = $in | Set-ClipboardText -PassThru Get-ClipboardText -Raw | Should -BeExactly $in $out | Should -BeExactly $in } } Describe CommandInputTest { It "Copies and pastes a PowerShell command's output correctly" { Get-Item / | Set-ClipboardText # Note: Inside Set-ClipboardText we remove the trailing newline that # Out-String invariably adds, so we must do the same here. $shouldBe = (Get-Item / | Out-String) -replace '\r?\n\z' $pasted = Get-ClipboardText -Raw $pasted | Should -BeExactly $shouldBe } It "Copies and pastes an external program's output correctly" { # Note: whoami without argument works on all supported platforms. whoami | Set-ClipboardText $shouldBe = whoami $is = Get-ClipboardText $is | Should -Be $shouldBe } } Describe OutputWidthTest { BeforeAll { # A custom object that is implicitly formatted with Format-Table with # 2 columns. $obj = [pscustomobject] @{ one = '1' * 40; two = '2' * 216 } } It "Truncates lines that are too wide for the specified width" { $obj | Set-ClipboardText -Width 80 (Get-ClipboardText)[3] | Should -BeLikeExactly '*...' } It "Allows incrementing the width to accommodate wider lines" { $obj | Set-ClipboardText -Width 257 # 40 + 1 (space between columns) + 216 (Get-ClipboardText)[3].TrimEnd() | Should -BeLikeExactly '*2' } } # Note: These tests apply to PS *Core* only, because Windows PowerShell doesn't require external utilities for clipboard support. Describe MissingExternalUtilityTest { # On *Windows PowerShell*, in which case we skip the # tests, because Windows Powershell does't require external utilities for # access to the clipboard. # Note: We don't exit right away, because do want to invoke the `It` block # with `-Skip` set to $True, so that the results indicated that the test # was deliberately skipped. if (-not $isWinPs) { # Determine the name of the module being tested. # For a Mock to be effective in the target module's context, it must be # defined with -ModuleName <name>. $thisModuleName = (Split-Path -Leaf $PSScriptRoot) # Define the platform-appropiate mocks. # Note that the conditional Mocks are necessary, because the Mock function # requires that the targeted command *exist*. if ($env:OS -eq 'Windows_NT') { # !! As of Pester v4.3.1, even though the `Mock` is defined "extension-less" # !! as `cmd` rather than `cmd.exe`, only `cmd.exe` invocations are covered. # !! See https://github.com/pester/Pester/issues/1043 Mock cmd { & $env:SystemRoot\System32\cmd.exe /c 'nosuchexe' } -ModuleName $thisModuleName } else { Mock sh { /bin/sh -c 'nosuchutil' } -ModuleName $thisModuleName } } It "PS Core: Generates a statement-terminating error when the required external utility is not present" -Skip:$isWinPs { { 'dummy' | Set-ClipboardText 2>$null } | Should -Throw } } Describe MTAtests { # Windows PowerShell: # A WinForms text-box workaround is needed when PowerShell is running in COM MTA # (multi-threaded apartment) mode. # By default, v2 runs in MTA mode and v3+ in STA mode. # However, you can *opt into* MTA mode in v3+, and the workaround is then needed too. # (In PSCore on Windows, MTA is the default again, but it has no access to WinForms # anyway and uses external utility clip.exe instead.) It "Windows PowerShell: Works in MTA mode" -Skip:(-not $isWinPs -or $PSVersionTable.PSVersion.Major -eq 2) { # Recursively invokes the 'StringInputTest' tests. # !! This produces NO OUTPUT; to troubleshoot, run the command interactively from the project folder. powershell.exe -noprofile -MTA -Command "if ([threading.thread]::CurrentThread.ApartmentState.ToString() -ne 'MTA') { Throw "Not in MTA mode." }; Invoke-Pester -Name StringInputTest -EnableExit" $LASTEXITCODE | Should -Be 0 } } Describe v2Tests { # Invoke these tests in *WinPS v2*, which amounts to a RECURSION. # Therefore, EXECUTION TAKES A WHILE. It "Windows PowerShell: Passes all tests in v2 as well." -Skip:(-not $isWinPs -or $PSVersionTable.PSVersion.Major -eq 2) { # !! An Install-Module-installed Pester is located in a version-named subfolder, which v2 cannot # !! detect, so we import Pester by explicit path. # !! Also `-version 2` must be the *first* argument passed to `powershell.exe`. # # !! NO OUTPUT IS PRODUCED - to troubleshoot, run the command interactively from the project folder. # !! Notably, *prior installation of v2 support is needed*, and PowerShell seems to quietly ignore `-version 2` # !! in its absence, so we have to test from *within* the session. powershell.exe -version 2 -noprofile -Command "Set-StrictMode -Version Latest; Import-Module '$((Get-Module Pester).Path)'; if (`$PSVersionTable.PSVersion.Major -ne 2) { Throw 'v2 SUPPORT IS NOT INSTALLED.' }; Invoke-Pester" $LASTEXITCODE | Should -Be 0 } } |