diff --git a/.gitignore b/.gitignore index 8fd24e879..749a82be7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ PSKoans.psproj PSKoans/*/PSKoans-help.xml + +Output/** +Tests/out/** +testResults.xml diff --git a/PSKoans/Private/Invoke-Koan.ps1 b/PSKoans/Private/Invoke-Koan.ps1 index f385ebbb9..fe5f0399b 100644 --- a/PSKoans/Private/Invoke-Koan.ps1 +++ b/PSKoans/Private/Invoke-Koan.ps1 @@ -11,7 +11,7 @@ Defines the hashtable that will be splatted into Invoke-Pester in the new PowerShell instance. .EXAMPLE - Invoke-Koan @{ Script = '.\AboutArrays.Koans.ps1'; PassThru = $true; Show = 'None' } + Invoke-Koan @{ Path = '.\AboutArrays.Koans.ps1'; PassThru = $true; Show = 'None' } Triggers Pester to assess the AboutArrays file in the current directory and pass back the complete tests object, hiding the standard test results display. @@ -33,7 +33,7 @@ end { try { $Requirements = [System.Management.Automation.Language.Parser]::ParseFile( - $ParameterSplat.Script, + $ParameterSplat.Path, [ref]$null, [ref]$null ).Ast.ScriptRequirements diff --git a/PSKoans/Public/Get-Blank.ps1 b/PSKoans/Public/Get-Blank.ps1 index 2ddc46a5f..7dfba8e9f 100644 --- a/PSKoans/Public/Get-Blank.ps1 +++ b/PSKoans/Public/Get-Blank.ps1 @@ -1,4 +1,43 @@ function Get-Blank { + <# + .SYNOPSIS + Gets a blank item that does not equal anything. + + .DESCRIPTION + Get-Blank returns an object of type [Blank] as defined in the PSKoans module. + This object is not equivalent to any other type of object, including itself, when compared + with a standard `-eq` comparison. + + The only exception, which is unavoidable, is that it is considered equal to $true when + $true is on the left-hand side of the comparison. This kind of comparison may sometimes + need to be carefully avoided when framing a koan assertion. + + For instance,an assertion such as `____ | Should -BeTrue` WILL pass, although it should not. + + .PARAMETER |PipeInput + Used to capture the input in a pipeline context, to avoid erroring out in those contexts. + This parameter is not intended to be used directly, and captures all pipeline input. + + .PARAMETER |ParameterInput + Used to capture parameter names and arguments when used as a substitute for any other cmdlet. + This parameter is not intended to be used directly, and collects all argument names and values. + + .EXAMPLE + Get-Blank + + Returns a blank object. + + .EXAMPLE + __ + + Returns a blank object. + + .NOTES + Author: Joel Sallow (@vexx32) + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Get-Blank.md')] [OutputType('Blank')] [Alias('__', '____', 'FILL_ME_IN')] diff --git a/PSKoans/Public/Get-Karma.ps1 b/PSKoans/Public/Get-Karma.ps1 index f24ddc121..5bb35ef28 100644 --- a/PSKoans/Public/Get-Karma.ps1 +++ b/PSKoans/Public/Get-Karma.ps1 @@ -1,4 +1,45 @@ function Get-Karma { + <# + .SYNOPSIS + Retrieves information about your progress in PSKoans. + + .DESCRIPTION + Get-Karma executes Pester against the koans and outputs a short report on your current progress. + + .PARAMETER IncludeModule + Get Karma for the default PowerShell Koans as well as Koans for the specified module. + Wildcards are supported. + + .PARAMETER List + Output a complete list of available koan topics. + + .PARAMETER Module + Get Karma for the specified module only. + Wildcards are supported. + + .PARAMETER Topic + Execute koans only from the selected Topic(s). + Wildcard patterns are permitted. + + .EXAMPLE + Get-Karma + + Outputs a hashtable containing information about your progress. + + .EXAMPLE + Get-Karma -List + + Outputs a list of koan topics, including both the user file location and the module file location. + + .NOTES + Author: Joel Sallow (@vexx32) + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Get-Karma.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(DefaultParameterSetName = 'Default', HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Get-Karma.md')] [OutputType('PSKoans.Result', 'PSKoans.CompleteResult')] @@ -109,7 +150,7 @@ # Execute in a fresh scope to prevent internal secrets being leaked $PesterTests = Invoke-Koan @{ - Script = $KoanFile.Path + Path = $KoanFile.Path PassThru = $true Output = 'None' } diff --git a/PSKoans/Public/Get-PSKoan.ps1 b/PSKoans/Public/Get-PSKoan.ps1 index 49645d3d3..f763d5575 100644 --- a/PSKoans/Public/Get-PSKoan.ps1 +++ b/PSKoans/Public/Get-PSKoan.ps1 @@ -1,4 +1,70 @@ function Get-PSKoan { + <# + .SYNOPSIS + Gets koan topic metadata for each topic. + + .DESCRIPTION + Get-PSKoan finds Koans in either the Module or User locations. + Koan information includes position and module information, as well as topic name. + + .PARAMETER IncludeModule + Get default PowerShell Koans as well as Koans for the specified module. + Wildcards are supported. + + .PARAMETER ListModules + List the modules included with PSKoans. + + .PARAMETER Module + Get Koans for the specified module only. + Wildcards are supported. + + .PARAMETER Scope + Get koans from the specified scope. + The default scope is Module. + User scope gets Koan information from the location used by Get-PSKoanLocation. + + .PARAMETER SkipAttributeParsing + By default, Get-PSKoan attempts to retrieve the Position and Module information from the Koan attribute in each file. + This process may be skipped by using this parameter. + + .PARAMETER Topic + Reset the specified topic or topics. + Wildcards are supported. + + .EXAMPLE + Get-PSKoan + + Get all Koans in the PSKoans module, excluding koans for individual modules. + + .EXAMPLE + Get-PSKoan -IncludeModule * + + Get all Koans in the PSKoans module, include all koans for individual PowerShell modules. + + .EXAMPLE + Get-PSKoan -Topic AboutArrays + + Get information about the AboutArrays koans. + + .EXAMPLE + Get-PSKoan -Module ActiveDirectory + + Get koans from the ActiveDirectory module only. + + .EXAMPLE + Get-PSKoan -Scope User + + Get all Koans in the User location, excluding koans for individual modules. + + .NOTES + Author: Chris Dent (@indented-automation) + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Get-PSKoan.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(DefaultParameterSetName = 'IncludeModule', HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Get-PSKoan.md')] [OutputType('PSKoans.KoanInfo')] diff --git a/PSKoans/Public/Get-PSKoanLocation.ps1 b/PSKoans/Public/Get-PSKoanLocation.ps1 index d57f4b70a..4c3e03688 100644 --- a/PSKoans/Public/Get-PSKoanLocation.ps1 +++ b/PSKoans/Public/Get-PSKoanLocation.ps1 @@ -1,4 +1,25 @@ function Get-PSKoanLocation { + <# + .SYNOPSIS + Gets the folder location where the current user's copy of the PSKoans lessons are stored. + + .DESCRIPTION + Gets the current value of the PSKoans working library path. + This value defaults to `$HOME\PSKoans` but can be changed as you prefer. + + .EXAMPLE + Get-PSKoanLocation + + C:\Users\Timmy\PSKoans + + Displays the path to the current user's koan library location. + + .NOTES + Author: Joel Sallow (@vexx32) + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Get-PSKoanLocation.md')] [OutputType([string])] param() diff --git a/PSKoans/Public/Get-PSKoanSetting.ps1 b/PSKoans/Public/Get-PSKoanSetting.ps1 index 63be82d0f..f93f8aeda 100644 --- a/PSKoans/Public/Get-PSKoanSetting.ps1 +++ b/PSKoans/Public/Get-PSKoanSetting.ps1 @@ -1,4 +1,38 @@ function Get-PSKoanSetting { + <# + .SYNOPSIS + Retrieves the configuration settings for PSKoans. + + .DESCRIPTION + Retrieves configuration data from the locally stored json file in `$HOME/.config/PSKoans`. + + .PARAMETER Name + Specifies which setting value to retrieve. + + .EXAMPLE + Get-PSKoanSetting + + Retrieves all module settings. + + .EXAMPLE + Get-PSKoanSetting -Name LibraryFolder + + Retrieves the library folder location (also retrievable with `Get-PSKoanLocation`). + + .EXAMPLE + Get-PSKoanSetting -Name Editor + + Retrieves the text editor that PSKoans will use for `Show-Karma -Contemplate`. + + .NOTES + Author: Joel Sallow (@vexx32) + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Set-PSKoanSetting.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Get-PSKoanSetting.md')] [OutputType([string], [PSCustomObject])] param( diff --git a/PSKoans/Public/Move-PSKoanLibrary.ps1 b/PSKoans/Public/Move-PSKoanLibrary.ps1 index 8f5d31f23..5beb3e645 100644 --- a/PSKoans/Public/Move-PSKoanLibrary.ps1 +++ b/PSKoans/Public/Move-PSKoanLibrary.ps1 @@ -1,4 +1,33 @@ function Move-PSKoanLibrary { + <# + .SYNOPSIS + Move your entire current PSKoans library folder to another location and update your KoanLocation setting to reflect the new location. + + .DESCRIPTION + `Move-PSKoanLibrary` takes your current PSKoans library location and moves the folder to the specified destination. + Then, it updates the current KoanLocation setting to point to the new location. + + .PARAMETER Path + The path to the new library location. + This path can be relative to the current session location, but cannot contain wildcards. + + .EXAMPLE + Move-PSKoanLibrary -Path C:\Users\Joe\OneDrive + + Moves Joe's koan library into his OneDrive directory. + + .NOTES + Author: Joel Sallow (@vexx32) + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Set-PSKoanSetting.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Get-PSKoanSetting.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Move-PSKoanLibrary.md')] [OutputType([void])] diff --git a/PSKoans/Public/Register-Advice.ps1 b/PSKoans/Public/Register-Advice.ps1 index 8c4a88083..9c35070df 100644 --- a/PSKoans/Public/Register-Advice.ps1 +++ b/PSKoans/Public/Register-Advice.ps1 @@ -1,4 +1,29 @@ function Register-Advice { + <# + .SYNOPSIS + Causes powershell to write a random piece of advice on each start. + + .DESCRIPTION + Causes powershell to write a random piece of advice on each start. + This is done by creating / modifying the powershell profile to call `Show-Advice` on each session start. + + .PARAMETER TargetProfile + Specify a named profile to modify. + + .EXAMPLE + Register-Advice + + Causes powershell to write a random piece of advice on each start. + + .NOTES + Author: Friedrich Weinmann (@FriedrichWeinmann) + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Show-Advice.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low', HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Register-Advice.md')] [OutputType([void])] diff --git a/PSKoans/Public/Reset-PSKoan.ps1 b/PSKoans/Public/Reset-PSKoan.ps1 index d57052152..68b2ba6b5 100644 --- a/PSKoans/Public/Reset-PSKoan.ps1 +++ b/PSKoans/Public/Reset-PSKoan.ps1 @@ -1,4 +1,78 @@ function Reset-PSKoan { + <# + .SYNOPSIS + Reset one or more koans or koan topics to the initial state. + + .DESCRIPTION + Replaces the koan in the user file set with the original copy of the koan from the module. + + .PARAMETER Context + Reset koans in the specified `Context` block. + + .PARAMETER IncludeModule + Reset the default PowerShell Koans as well as Koans for the specified module. + Wildcards are supported. + + .PARAMETER Module + Reset Koans for the specified module only. + Wildcards are supported. + + .PARAMETER Name + The name of the koan to reset. + Wildcards are supported. + + .PARAMETER Topic + Reset the specified topic or topics. + Wildcards are supported. + + .EXAMPLE + Reset-PSKoan + + Completely reset all koans in the user folder to the initial state. + You will be prompted to confirm. + + .EXAMPLE + Reset-PSKoan -Topic AboutArrays + + Resets all koans in the AboutArrays topic. + + .EXAMPLE + Reset-PSKoan -Topic AboutArrays, AboutComparison + + Reset all koans in the AboutArrays and AboutComparison topics. + + .EXAMPLE + Reset-PSKoan -Topic AboutArrays -Name 'allows the collection to be split into multiple parts' + + Resets the "allows the collection to be split into multiple parts" koan in the AboutArrays topic. + + .EXAMPLE + Reset-PSKoan -Topic AboutComparison -Name 'may coerce values to boolean' -Context '-and' + + Resets the "may coerce values to boolean" koan in the "-and" context of the AboutComparison topic. + + .EXAMPLE + Reset-PSKoan -Topic AboutComparison -Context '-and' + + Resets all koans in the "-and" context of the AboutComparison topic. + + .EXAMPLE + Reset-PSKoan -Topic AboutC* -Name returns* + + Reset koans with names starting "returns" in topics matching the wildcard pattern "AboutC*". + + .NOTES + Author: Chris Dent (@indented-automation) + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Get-PSKoan.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Update-PSKoan.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Reset-PSKoan.md', PositionalBinding = $false, diff --git a/PSKoans/Public/Set-PSKoanLocation.ps1 b/PSKoans/Public/Set-PSKoanLocation.ps1 index 444634373..a63c60d88 100644 --- a/PSKoans/Public/Set-PSKoanLocation.ps1 +++ b/PSKoans/Public/Set-PSKoanLocation.ps1 @@ -1,4 +1,41 @@ function Set-PSKoanLocation { + <# + .SYNOPSIS + Sets the PSKoans folder location where koan lesson files will be stored and retrieved. + + .DESCRIPTION + Sets the `KoanLocation` configuration setting in order to modify where the module looks for and stores its koan lesson files. + + .PARAMETER PassThru + Whether the function should pass the provided `-Path` value down the pipe when the configuration has been changed. + + .PARAMETER Path + Specify the path to set the koan location to. + + .EXAMPLE + Set-PSKoanLocation -Path C:\PSKoans + + Measure-Karma + + Sets the koan folder location to 'C:\PSKoans' and then invokes Measure-Karma to examine that location for koan files. + + .NOTES + Author: Joel Sallow (@vexx32) + + The PSKoans folder specified will become the location to look for koans files. + If this location is empty or nonexistent, it will be created and populated with a pristine copy of the koans library when Measure-Karma is run next. + + You can optionally populate it yourself by running `Show-Karma -Reset` following use of this cmdlet. + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Get-PSKoanLocation.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Move-PSKoanLibrary.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Set-PSKoanLocation.md')] [OutputType([void])] diff --git a/PSKoans/Public/Set-PSKoanSetting.ps1 b/PSKoans/Public/Set-PSKoanSetting.ps1 index 404c271fe..c96e81392 100644 --- a/PSKoans/Public/Set-PSKoanSetting.ps1 +++ b/PSKoans/Public/Set-PSKoanSetting.ps1 @@ -1,4 +1,44 @@ function Set-PSKoanSetting { + <# + .SYNOPSIS + Modifies the configuration settings for PSKoans. + + .DESCRIPTION + Sets module configuration data in a JSON file in the user's $HOME directory. + + .PARAMETER Name + Specifies which setting value to modify. + + .PARAMETER Reset + Resets the user's settings to the default values. + + .PARAMETER Settings + A hashtable containing one or more settings to modify and their values. + + .PARAMETER Value + Provides a value to apply to the target setting. + + .EXAMPLE + Set-PSKoanSetting -Name LibraryFolder -Value "./PSKoans" + + Sets the library folder location to the `PSKoans` folder in the current directory. + + .EXAMPLE + Set-PSKoanSetting -Name Editor -Value "atom" + + Sets the text editor used for `Show-Karma -Contemplate` to GitHub Atom. For a + list of text editors known to PSKoans, see Example 2 in the documentation for + [Show-Karma -Contemplate](https://github.com/vexx32/PSKoans/tree/main/docs/Show-Karma.md). + + .NOTES + Author: Joel Sallow (@vexx32) + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Get-PSKoanSetting.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', DefaultParameterSetName = 'Single', HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Set-PSKoanSetting.md')] [OutputType([void])] diff --git a/PSKoans/Public/Show-Advice.ps1 b/PSKoans/Public/Show-Advice.ps1 index 3e386ab6c..9f66ea864 100644 --- a/PSKoans/Public/Show-Advice.ps1 +++ b/PSKoans/Public/Show-Advice.ps1 @@ -1,4 +1,29 @@ function Show-Advice { + <# + .SYNOPSIS + Prints a piece of advice to the screen. + + .DESCRIPTION + Prints a piece of advice to the screen. + Advice snippets are stored in a small library file in the module folder. + + .PARAMETER Name + The title or name of the specific advice snippet to display. + + .EXAMPLE + Get-Advice + + Print a random piece of advice to the screen. + + .NOTES + Author: Friedrich Weinmann (@FriedrichWeinmann) + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Register-Advice.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Show-Advice.md')] [Alias('Get-Advice')] [OutputType([void])] diff --git a/PSKoans/Public/Show-Karma.ps1 b/PSKoans/Public/Show-Karma.ps1 index d0dcec6cd..98224d33f 100644 --- a/PSKoans/Public/Show-Karma.ps1 +++ b/PSKoans/Public/Show-Karma.ps1 @@ -1,4 +1,76 @@ function Show-Karma { + <# + .SYNOPSIS + Reflect on your progress and check your answers. + + .DESCRIPTION + Show-Karma executes Pester against the koans to evaluate if you have made the necessary corrections for success. + The default output mode is to the information stream, with decorated flavour text and progress information. + + If you want a more data-oriented results report, use `Get-Karma` instead. + + .PARAMETER ClearScreen + Clears the console host before displaying the meditation prompt. + + .PARAMETER Contemplate + Opens your local koans library. + If VS Code is installed, it will start VS Code in the folder. + Otherwise, the folder is simply opened in a file explorer. + If you have VS Code Insiders installed, you can set `$env:PSKoans_EditorPreference = "code-insiders"` to indicate VS Code Insiders should be opened instead. + + .PARAMETER Detailed + Adds a summarized view of the current topic file to the meditation prompt. + The summary will contain a full list of all koans in the file, and indicate their current status. + + .PARAMETER IncludeModule + Show Karma for the default PowerShell Koans as well as Koans for the specified module. + Wildcards are supported. + + .PARAMETER Library + Opens the current `KoanLocation` folder in the preferred editor. + To set the preferred editor, use `Set-PSKoanSetting`. + If the preferred editor cannot be found or the setting is cleared, the folder will be opened in the default handler. + This should be Windows Explorer on Windows, Finder on Mac, etc. + + .PARAMETER List + Output a complete list of available koan topics. + + .PARAMETER Module + Show Karma for Koans in the specified module only. + Wildcards are supported. + + .PARAMETER Topic + Execute koans only from the selected Topic(s). + Wildcard patterns are permitted. + When provided along with `-Contemplate`, the targeted topic will be respected. + + .EXAMPLE + Show-Karma + + Assesses the koan lessons, and displays the meditation prompt with the results. + + .EXAMPLE + Show-Karma -Contemplate + + Opens the current koan file in the editor specified by the `Editor` setting. + Use `Set-PSKoanSetting` to change the editor used. + + If a known editor (`code`, `code-insiders`, `codium`, or `atom`) is used, PSKoans will pass along line information as well. + + .EXAMPLE + Show-Karma -Contemplate -Topic AboutComparison + + Opens the specified `AboutComparison` topic file in the preferred editor. + + .NOTES + Author: Joel Sallow (@vexx32) + + .LINK + https://github.com/vexx32/PSKoans/blob/main/docs/Get-Karma.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(DefaultParameterSetName = 'Default', HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Show-Karma.md')] [OutputType([void])] diff --git a/PSKoans/Public/Update-PSKoan.ps1 b/PSKoans/Public/Update-PSKoan.ps1 index 7279736b0..ce0b14762 100644 --- a/PSKoans/Public/Update-PSKoan.ps1 +++ b/PSKoans/Public/Update-PSKoan.ps1 @@ -1,6 +1,55 @@ using namespace System.Collections.Generic function Update-PSKoan { + <# + .SYNOPSIS + Update the user Koan directory with new topics and koans. + + .DESCRIPTION + Update the user Koan directory with new topics. + Topics will be moved to new directories if appropriate. + Old files will be removed. + + Existing koan topics are updated with new koans. + Progress is preserved as much as possible. + + .PARAMETER IncludeModule + Update the default PowerShell Koans as well as Koans for the specified module. + Wildcards are supported. + + .PARAMETER Module + Update Koans in the specified module only. + Wildcards are supported. + + .PARAMETER Topic + Updates the specified topic from the module. + Wildcards are supported. + + .EXAMPLE + Update-PSKoan -Topic AboutCompareObject + + The topic AboutCompareObject will be added if it is not already present. + If it is already present, the current copy will be compared to the base module copy. + If any koans are missing from the user's copy, they will be added. + If any koans have been removed from the module copy, they will be removed from the user's copy. + + .EXAMPLE + Update-PSKoan + + All missing topics and koans will be copied from the module. + + .NOTES + Author: Chris Dent (@indented-automation) + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Get-PSKoan.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/Reset-PSKoan.md + + .LINK + https://github.com/vexx32/PSKoans/tree/main/docs/PSKoans.md + #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'TopicOnly', ConfirmImpact = "High", HelpUri = 'https://github.com/vexx32/PSKoans/tree/main/docs/Update-PSKoan.md')] [OutputType([void])] diff --git a/Tests/Functions/Private/Assert-UnblockedFile.Tests.ps1 b/Tests/Functions/Private/Assert-UnblockedFile.Tests.ps1 index 2ab1b9ea7..67bf2744c 100644 --- a/Tests/Functions/Private/Assert-UnblockedFile.Tests.ps1 +++ b/Tests/Functions/Private/Assert-UnblockedFile.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} #region Discovery $SkipTests = $PSVersionTable.PSEdition -ne 'Desktop' -or $PSVersionTable.Platform -ne 'Win32NT' diff --git a/Tests/Functions/Private/ConvertFrom-WildcardPattern.Tests.ps1 b/Tests/Functions/Private/ConvertFrom-WildcardPattern.Tests.ps1 index 96ac71f97..94fd3f29c 100644 --- a/Tests/Functions/Private/ConvertFrom-WildcardPattern.Tests.ps1 +++ b/Tests/Functions/Private/ConvertFrom-WildcardPattern.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'ConvertFrom-WildcardPattern' { diff --git a/Tests/Functions/Private/Get-KoanAst.Tests.ps1 b/Tests/Functions/Private/Get-KoanAst.Tests.ps1 index cb16f9339..cd0d8f5fd 100644 --- a/Tests/Functions/Private/Get-KoanAst.Tests.ps1 +++ b/Tests/Functions/Private/Get-KoanAst.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Get-KoanAst' { diff --git a/Tests/Functions/Private/Get-KoanAttribute.Tests.ps1 b/Tests/Functions/Private/Get-KoanAttribute.Tests.ps1 index 2ba7d11ff..5e84dde68 100644 --- a/Tests/Functions/Private/Get-KoanAttribute.Tests.ps1 +++ b/Tests/Functions/Private/Get-KoanAttribute.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Get-KoanAttribute' { diff --git a/Tests/Functions/Private/Get-KoanIt.Tests.ps1 b/Tests/Functions/Private/Get-KoanIt.Tests.ps1 index 6f902cb98..56d3089cf 100644 --- a/Tests/Functions/Private/Get-KoanIt.Tests.ps1 +++ b/Tests/Functions/Private/Get-KoanIt.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Get-KoanIt' { diff --git a/Tests/Functions/Private/Invoke-Koan.Tests.ps1 b/Tests/Functions/Private/Invoke-Koan.Tests.ps1 index 10e58523d..2d4c690eb 100644 --- a/Tests/Functions/Private/Invoke-Koan.Tests.ps1 +++ b/Tests/Functions/Private/Invoke-Koan.Tests.ps1 @@ -1,46 +1,56 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } +# cspell:ignore BHPS + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Invoke-Koan' { + InModuleScope 'PSKoans' { + BeforeAll { + $script:controlTest = Resolve-Path "$PSScriptRoot/ControlTests/Invoke-Koan.Control_Tests.ps1" + } - BeforeAll { - $testFile = @{ Script = "$PSScriptRoot/ControlTests/Invoke-Koan.Control_Tests.ps1" } - } + It 'runs the test successfully' { + { Invoke-Koan -ParameterSplat @{ Path = $script:controlTest } } | Should -Not -Throw + } - It 'runs the test successfully' { - { - InModuleScope 'PSKoans' -Parameters $testFile { - param($Script) - Invoke-Koan @{ Script = $Script } - } - } | Should -Not -Throw - } + It 'produces output with -PassThru' { + Invoke-Koan -ParameterSplat @{ Path = $script:controlTest; PassThru = $true } | Should -Not -BeNullOrEmpty + } - It 'produces output with -Passthru' { - InModuleScope 'PSKoans' -Parameters $testFile { - param($Script) - Invoke-Koan @{ Script = $Script; PassThru = $true } - } | Should -Not -BeNullOrEmpty - } + It 'correctly reports test results' { + $Results = Invoke-Koan -ParameterSplat @{ Path = $script:controlTest; PassThru = $true } - It 'correctly reports test results' { - $Results = InModuleScope 'PSKoans' -Parameters $testFile { - param($Script) - Invoke-Koan @{ Script = $Script; PassThru = $true } + $Results.TotalCount | Should -Be 2 + $Results.PassedCount | Should -Be 0 + $Results.FailedCount | Should -Be 2 } - $Results.TotalCount | Should -Be 2 - $Results.PassedCount | Should -Be 0 - $Results.FailedCount | Should -Be 2 - } + It 'reports only expected exception types' { + $Results = Invoke-Koan -ParameterSplat @{ Path = $script:controlTest; PassThru = $true } - It 'reports only expected exception types' { - $Results = InModuleScope 'PSKoans' -Parameters $testFile { - param($Script) - Invoke-Koan @{ Script = $Script; PassThru = $true } + $Results.Tests.ErrorRecord.Exception | + ForEach-Object -MemberName GetType | + Should -Be @([Exception], [NotImplementedException]) } - - $Results.Tests.ErrorRecord.Exception | - ForEach-Object -MemberName GetType | - Should -Be @([Exception], [NotImplementedException]) } } diff --git a/Tests/Functions/Private/Measure-Koan.Tests.ps1 b/Tests/Functions/Private/Measure-Koan.Tests.ps1 index 577a82d60..393e8b18e 100644 --- a/Tests/Functions/Private/Measure-Koan.Tests.ps1 +++ b/Tests/Functions/Private/Measure-Koan.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Measure-Koan' -Skip { diff --git a/Tests/Functions/Private/New-KoanRunspace.Tests.ps1 b/Tests/Functions/Private/New-KoanRunspace.Tests.ps1 index 6155de163..a1e378ef7 100644 --- a/Tests/Functions/Private/New-KoanRunspace.Tests.ps1 +++ b/Tests/Functions/Private/New-KoanRunspace.Tests.ps1 @@ -1,3 +1,27 @@ +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} + Describe 'New-KoanRunspace' { BeforeAll { diff --git a/Tests/Functions/Private/New-PSKoanErrorRecord.Tests.ps1 b/Tests/Functions/Private/New-PSKoanErrorRecord.Tests.ps1 index 331aa9f05..98d596ed2 100644 --- a/Tests/Functions/Private/New-PSKoanErrorRecord.Tests.ps1 +++ b/Tests/Functions/Private/New-PSKoanErrorRecord.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'New-PSKoanErrorRecord' { diff --git a/Tests/Functions/Private/Update-PSKoanFile.Tests.ps1 b/Tests/Functions/Private/Update-PSKoanFile.Tests.ps1 index 357df55bd..09c5659b8 100644 --- a/Tests/Functions/Private/Update-PSKoanFile.Tests.ps1 +++ b/Tests/Functions/Private/Update-PSKoanFile.Tests.ps1 @@ -1,24 +1,47 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Update-PSKoanFile' { BeforeAll { - Mock 'Get-PSKoanLocation' { - Join-Path -Path $TestDrive -ChildPath 'Koans' - } + $koanLocation = Join-Path -Path $TestDrive -ChildPath 'Koans' + $koanRelativePath = 'Group/AboutSomething.ps1' + $moduleKoanPath = Join-Path -Path $TestDrive -ChildPath 'Module/Group/AboutSomething.ps1' - Mock 'Get-PSKoan' { + Mock 'Get-PSKoanLocation' -ModuleName 'PSKoans' { $koanLocation } + Mock 'Get-PSKoan' -ModuleName 'PSKoans' { [PSCustomObject]@{ Topic = 'AboutSomething' - Path = Join-Path -Path $TestDrive -ChildPath 'Module/Group/AboutSomething.ps1' - RelativePath = 'Group/AboutSomething.ps1' + Path = $moduleKoanPath + RelativePath = $koanRelativePath } } New-Item -Path (Join-Path -Path $TestDrive -ChildPath 'Koans/Group') -ItemType Directory New-Item -Path (Join-Path -Path $TestDrive -ChildPath 'Module/Group') -ItemType Directory - Set-Content -Path (Get-PSKoan).Path -Value @' + Set-Content -Path $moduleKoanPath -Value @' Describe 'AboutSomething' { It 'koan 1' { __ | Should -Be 1 @@ -42,7 +65,7 @@ Describe 'Update-PSKoanFile' { } '@ - $userFilePath = Join-Path -Path (Get-PSKoanLocation) -ChildPath (Get-PSKoan).RelativePath + $userFilePath = Join-Path -Path $koanLocation -ChildPath $koanRelativePath } BeforeEach { diff --git a/Tests/Functions/Public/Get-Blank.Tests.ps1 b/Tests/Functions/Public/Get-Blank.Tests.ps1 index f24e30556..d0f4a8ec4 100644 --- a/Tests/Functions/Public/Get-Blank.Tests.ps1 +++ b/Tests/Functions/Public/Get-Blank.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Get-Blank' { diff --git a/Tests/Functions/Public/Get-Karma.Tests.ps1 b/Tests/Functions/Public/Get-Karma.Tests.ps1 index 7a25cce12..f04982297 100644 --- a/Tests/Functions/Public/Get-Karma.Tests.ps1 +++ b/Tests/Functions/Public/Get-Karma.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Get-Karma' { @@ -52,18 +74,18 @@ Describe 'Get-Karma' { BeforeAll { Mock 'Measure-Koan' -ModuleName 'PSKoans' - Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'User' } - Mock 'Update-PSKoan' { throw 'Prevent recursion' } - Mock 'Write-Warning' + Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'User' } -ModuleName 'PSKoans' + Mock 'Update-PSKoan' { throw 'Prevent recursion' } -ModuleName 'PSKoans' + Mock 'Write-Warning' -ModuleName 'PSKoans' } It 'should attempt to populate koans and then recurse to reassess' { { Get-Karma } | Should -Throw -ExpectedMessage 'Prevent recursion' - Should -Invoke 'Update-PSKoan' -Scope Context + Should -Invoke 'Update-PSKoan' -Scope Context -ModuleName 'PSKoans' } It 'displays a warning before initiating a reset' { - Should -Invoke 'Write-Warning' -Scope Context + Should -Invoke 'Write-Warning' -Scope Context -ModuleName 'PSKoans' } It 'throws an error if a Topic is specified that matches nothing' { @@ -74,13 +96,13 @@ Describe 'Get-Karma' { Context 'With -ListTopics Parameter' { BeforeAll { - Mock 'Get-PSKoan' + Mock 'Get-PSKoan' -ModuleName 'PSKoans' } It 'lists all the koan topics' { Get-Karma -ListTopics - Should -Invoke 'Get-PSKoan' + Should -Invoke 'Get-PSKoan' -ModuleName 'PSKoans' } } @@ -104,13 +126,12 @@ Describe 'Get-Karma' { Context 'Behaviour When All Koans Are Completed' { BeforeAll { - Mock 'Get-PSKoanLocation' { - Join-Path -Path $TestDrive -ChildPath 'CompletedKoan' - } + $completedKoanLocation = Join-Path -Path $TestDrive -ChildPath 'CompletedKoan' + Mock 'Get-PSKoanLocation' -ModuleName 'PSKoans' { $completedKoanLocation } Mock 'Measure-Koan' -ModuleName 'PSKoans' -MockWith { 2 } - $TestFile = Join-Path -Path (Get-PSKoanLocation) -ChildPath 'Group\SelectedTopicTest.Koans.ps1' + $TestFile = Join-Path -Path $completedKoanLocation -ChildPath 'Group\SelectedTopicTest.Koans.ps1' New-Item -Path (Split-Path $TestFile -Parent) -ItemType Directory -Force Set-Content -Path $TestFile -Value @' using module PSKoans diff --git a/Tests/Functions/Public/Get-PSKoan.Tests.ps1 b/Tests/Functions/Public/Get-PSKoan.Tests.ps1 index 93bca3790..cca0db4bd 100644 --- a/Tests/Functions/Public/Get-PSKoan.Tests.ps1 +++ b/Tests/Functions/Public/Get-PSKoan.Tests.ps1 @@ -1,16 +1,37 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Get-PSKoan' { BeforeAll { - Mock 'Get-PSKoanLocation' { - Join-Path $TestDrive 'PSKoans' - } + $koanLocation = Join-Path $TestDrive 'PSKoans' + Mock 'Get-PSKoanLocation' -ModuleName 'PSKoans' { $koanLocation } Update-PSKoan -Confirm:$false # Stage test module - $path = Join-Path -Path (Get-PSKoanLocation) 'Modules\TestModule' + $path = Join-Path -Path $koanLocation 'Modules\TestModule' New-Item -Path $path -ItemType Directory -Force Set-Content -Path (Join-Path -Path $path -ChildPath 'AboutSomething.Koans.ps1') -Value @' using module PSKoans @@ -26,7 +47,7 @@ Describe 'Get-PSKoan' { } It 'retrieves all except module-specific koan files' { - $Files = Get-ChildItem -Path (Get-PSKoanLocation) -Filter *.Koans.ps1 -Recurse -File | + $Files = Get-ChildItem -Path $koanLocation -Filter *.Koans.ps1 -Recurse -File | Where-Object FullName -NotMatch 'PSKoans[\\/]Modules[\\/]' (Get-PSKoan).Topic.Count | Should -Be $Files.Count @@ -49,7 +70,7 @@ Describe 'Get-PSKoan' { } It 'should throw a terminating error if a file is blocked' -Skip:($PSVersionTable.PSEdition -ne 'Desktop' -or $PSVersionTable.Platform -ne 'Win32NT') { - $testFile = Get-ChildItem -Path (Get-PSKoanLocation) -Filter AboutArrays.Koans.ps1 -Recurse -File | + $testFile = Get-ChildItem -Path $koanLocation -Filter AboutArrays.Koans.ps1 -Recurse -File | Select-Object -First 1 Set-Content -Path $testFile.FullName -Stream Zone.Identifier -Value @' diff --git a/Tests/Functions/Public/Get-PSKoanLocation.Tests.ps1 b/Tests/Functions/Public/Get-PSKoanLocation.Tests.ps1 index d62e5be02..b9a9730a5 100644 --- a/Tests/Functions/Public/Get-PSKoanLocation.Tests.ps1 +++ b/Tests/Functions/Public/Get-PSKoanLocation.Tests.ps1 @@ -1,11 +1,33 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Get-PSKoanLocation' { Context 'Normal Behaviour' { BeforeAll { - Mock 'Get-PSKoanSetting' -ParameterFilter { $Name -eq 'KoanLocation' } -MockWith { + Mock 'Get-PSKoanSetting' -ParameterFilter { $Name -eq 'KoanLocation' } -ModuleName 'PSKoans' -MockWith { '~/PSKoans' } @@ -17,14 +39,14 @@ Describe 'Get-PSKoanLocation' { } It 'calls Get-PSKoanSetting with -Name "KoanLocation"' { - Should -Invoke 'Get-PSKoanSetting' -Scope Context + Should -Invoke 'Get-PSKoanSetting' -Scope Context -ModuleName 'PSKoans' } } Context 'No Value Available' { BeforeAll { - Mock 'Get-PSKoanSetting' -ParameterFilter { $Name -eq 'KoanLocation' } + Mock 'Get-PSKoanSetting' -ParameterFilter { $Name -eq 'KoanLocation' } -ModuleName 'PSKoans' } It 'throws an error if no value can be retrieved' { diff --git a/Tests/Functions/Public/Get-PSKoanSetting.Tests.ps1 b/Tests/Functions/Public/Get-PSKoanSetting.Tests.ps1 index e5b5e3efe..4c54224e3 100644 --- a/Tests/Functions/Public/Get-PSKoanSetting.Tests.ps1 +++ b/Tests/Functions/Public/Get-PSKoanSetting.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Get-PSKoanSetting' { @@ -22,7 +44,7 @@ Describe 'Get-PSKoanSetting' { Context 'Settings file does not exist' { BeforeAll { - Mock 'Set-PSKoanSetting' -ParameterFilter { $Settings -is [hashtable] } + Mock 'Set-PSKoanSetting' -ParameterFilter { $Settings -is [hashtable] } -ModuleName 'PSKoans' $DefaultSettings = InModuleScope 'PSKoans' { $script:DefaultSettings } } ` @@ -34,7 +56,7 @@ Describe 'Get-PSKoanSetting' { } It 'calls Set-PSKoanSetting to set the default settings' { - Should -Invoke 'Set-PSKoanSetting' -Scope Context + Should -Invoke 'Set-PSKoanSetting' -Scope Context -ModuleName 'PSKoans' } } diff --git a/Tests/Functions/Public/Move-PSKoanLibrary.Tests.ps1 b/Tests/Functions/Public/Move-PSKoanLibrary.Tests.ps1 index 4763e628e..4e9623568 100644 --- a/Tests/Functions/Public/Move-PSKoanLibrary.Tests.ps1 +++ b/Tests/Functions/Public/Move-PSKoanLibrary.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Move-PSKoanLibrary' { @@ -9,9 +31,9 @@ Describe 'Move-PSKoanLibrary' { Select-Object -ExpandProperty FullName $TestPath = New-Item -ItemType Directory -Path 'TestDrive:/TestPath' | Join-Path -ChildPath 'Koans' - Mock 'Get-PSKoanLocation' { $OriginalPath } - Mock 'Set-PSKoanLocation' -ParameterFilter { $Path -eq $TestPath } - Mock 'Move-Item' -ParameterFilter { $Path -eq $OriginalPath } -MockWith { $Destination } + Mock 'Get-PSKoanLocation' { $OriginalPath } -ModuleName 'PSKoans' + Mock 'Set-PSKoanLocation' -ParameterFilter { $Path -eq $TestPath } -ModuleName 'PSKoans' + Mock 'Move-Item' -ParameterFilter { $Path -eq $OriginalPath } -MockWith { $Destination } -ModuleName 'PSKoans' } It 'should output the new location' { @@ -19,15 +41,15 @@ Describe 'Move-PSKoanLibrary' { } It 'should call Get-PSKoanLocation' { - Should -Invoke 'Get-PSKoanLocation' -Scope Context + Should -Invoke 'Get-PSKoanLocation' -Scope Context -ModuleName 'PSKoans' } It 'should call Move-Item' { - Should -Invoke 'Move-Item' -Scope Context + Should -Invoke 'Move-Item' -Scope Context -ModuleName 'PSKoans' } It 'should call Set-PSKoanLocation' { - Should -Invoke 'Set-PSKoanLocation' -Scope Context + Should -Invoke 'Set-PSKoanLocation' -Scope Context -ModuleName 'PSKoans' } } diff --git a/Tests/Functions/Public/Register-Advice.Tests.ps1 b/Tests/Functions/Public/Register-Advice.Tests.ps1 index 326a1931f..5890198dc 100644 --- a/Tests/Functions/Public/Register-Advice.Tests.ps1 +++ b/Tests/Functions/Public/Register-Advice.Tests.ps1 @@ -1,13 +1,35 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe "Register-Advice" { Context "Profile Folder/File Missing" { BeforeAll { - Mock New-Item -Verifiable - Mock Test-Path { $false } -Verifiable - Mock Set-Content -ParameterFilter { $Value -eq "Show-Advice" } -Verifiable + Mock New-Item -Verifiable -ModuleName 'PSKoans' + Mock Test-Path { $false } -Verifiable -ModuleName 'PSKoans' + Mock Set-Content -ParameterFilter { $Value -eq "Show-Advice" } -Verifiable -ModuleName 'PSKoans' } It 'should create the $profile if it does not exist' { @@ -19,9 +41,9 @@ Describe "Register-Advice" { Context "Profile Already Exists" { BeforeAll { - Mock 'Test-Path' { $true } -Verifiable - Mock 'Select-String' { $false } -Verifiable - Mock 'Add-Content' -Verifiable + Mock 'Test-Path' { $true } -Verifiable -ModuleName 'PSKoans' + Mock 'Select-String' { $false } -Verifiable -ModuleName 'PSKoans' + Mock 'Add-Content' -Verifiable -ModuleName 'PSKoans' } It "adds content to the profile if it already exists (Get|Set)-Advice" { @@ -33,10 +55,11 @@ Describe "Register-Advice" { Context "Parameter Validation" { BeforeAll { - Mock Test-Path { $false } -ParameterFilter { $Path -eq $ProfileFolder } - Mock New-Item - Mock Test-Path { $false } -ParameterFilter { $Path -eq $ProfilePath } - Mock Set-Content -ParameterFilter { $Value -eq "Show-Advice" } + Mock Test-Path { $false } -ModuleName 'PSKoans' + Mock New-Item -ModuleName 'PSKoans' + Mock Select-String { $false } -ModuleName 'PSKoans' + Mock Add-Content -ModuleName 'PSKoans' + Mock Set-Content -ParameterFilter { $Value -eq "Show-Advice" } -ModuleName 'PSKoans' } It "throws if an invalid value is supplied for -TargetProfile" { diff --git a/Tests/Functions/Public/Reset-PSKoan.Tests.ps1 b/Tests/Functions/Public/Reset-PSKoan.Tests.ps1 index 885ab87c9..7dc9e7d99 100644 --- a/Tests/Functions/Public/Reset-PSKoan.Tests.ps1 +++ b/Tests/Functions/Public/Reset-PSKoan.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe Reset-PSKoan { @@ -7,14 +29,23 @@ Describe Reset-PSKoan { Confirm = $false } - Mock 'Get-PSKoanLocation' { - Join-Path -Path $TestDrive -ChildPath 'PSKoans' + $koanLocation = Join-Path -Path $TestDrive -ChildPath 'PSKoans' + $moduleKoanPath = Join-Path -Path $TestDrive -ChildPath 'Module\Group\AboutSomething.Koans.ps1' + + Mock 'Get-PSKoanLocation' -ModuleName 'PSKoans' { $koanLocation } + + Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'Module' } -ModuleName 'PSKoans' -MockWith { + [PSCustomObject]@{ + Topic = 'AboutSomething' + Path = $moduleKoanPath + RelativePath = 'Group\AboutSomething.Koans.ps1' + } } - Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'Module' } -MockWith { + Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'User' } -ModuleName 'PSKoans' -MockWith { [PSCustomObject]@{ Topic = 'AboutSomething' - Path = Join-Path -Path $TestDrive -ChildPath 'Module\Group\AboutSomething.Koans.ps1' + Path = $userFilePath RelativePath = 'Group\AboutSomething.Koans.ps1' } } @@ -22,9 +53,9 @@ Describe Reset-PSKoan { New-Item -Path (Join-Path -Path $TestDrive -ChildPath 'Module\Group') -ItemType Directory New-Item -Path (Join-Path -Path $TestDrive -ChildPath 'PSKoans\Group') -ItemType Directory - $userFilePath = Get-PSKoanLocation | Join-Path -ChildPath 'Group\AboutSomething.Koans.ps1' + $userFilePath = Join-Path -Path $koanLocation -ChildPath 'Group\AboutSomething.Koans.ps1' - Set-Content -Path (Get-PSKoan -Scope Module).Path, $userFilePath -Value @' + Set-Content -Path $moduleKoanPath, $userFilePath -Value @' using module PSKoans [Koan(Position = 1)] param ( ) @@ -56,43 +87,45 @@ Describe Reset-PSKoan { Context 'User file exists, It block exists' { BeforeAll { - Mock 'Set-Content' - Mock 'Copy-Item' + Mock 'Set-Content' -ModuleName 'PSKoans' + Mock 'Copy-Item' -ModuleName 'PSKoans' } It 'updates an existing user file when -Name is supplied' { Reset-PSKoan -Name 'existing content' @defaultParams - Should -Invoke 'Set-Content' -Times 1 - Should -Invoke 'Copy-Item' -Times 0 + Should -Invoke 'Set-Content' -Times 1 -ModuleName 'PSKoans' + Should -Invoke 'Copy-Item' -Times 0 -ModuleName 'PSKoans' } It 'updates an existing user file when -Context is supplied' { Reset-PSKoan -Context 'first' @defaultParams - Should -Invoke 'Set-Content' -Times 1 -Exactly - Should -Invoke 'Copy-Item' -Times 0 + Should -Invoke 'Set-Content' -Times 1 -Exactly -ModuleName 'PSKoans' + Should -Invoke 'Copy-Item' -Times 0 -ModuleName 'PSKoans' } It 'updates an existing user file when -Name and -Context are supplied' { Reset-PSKoan -Name 'nested reset content' -Context 'first' @defaultParams - Should -Invoke 'Set-Content' -Times 1 -Exactly - Should -Invoke 'Copy-Item' -Times 0 + Should -Invoke 'Set-Content' -Times 1 -Exactly -ModuleName 'PSKoans' + Should -Invoke 'Copy-Item' -Times 0 -ModuleName 'PSKoans' } It 'copies a koan file from the module when -Name and -Context are not supplied' { Reset-PSKoan @defaultParams - Should -Invoke 'Set-Content' -Times 0 - Should -Invoke 'Copy-Item' -Times 1 -Exactly + Should -Invoke 'Set-Content' -Times 0 -ModuleName 'PSKoans' + Should -Invoke 'Copy-Item' -Times 1 -Exactly -ModuleName 'PSKoans' } } Context 'User file exists, It block does not exist' { It 'writes a non-terminating error when the user file does not include the specified Koan' { - Mock 'Get-KoanIt' -ParameterFilter { $Path -match 'PSKoans' } -Module 'PSKoans' + $realGetKoanIt = InModuleScope 'PSKoans' { Get-Item -Path 'Function:\Get-KoanIt' } + Mock 'Get-KoanIt' -ModuleName 'PSKoans' -MockWith { & $realGetKoanIt @args } + Mock 'Get-KoanIt' -ParameterFilter { $Path -match 'PSKoans' } -ModuleName 'PSKoans' { Reset-PSKoan -Topic AboutSomething -Name 'existing content' -ErrorAction Stop @defaultParams } | Should -Throw -ErrorId 'PSKoans.UserItNotFound,Reset-PSKoan' @@ -104,8 +137,8 @@ Describe Reset-PSKoan { BeforeAll { New-Item "$TestDrive/DoesNotExist.Koans.ps1" -ItemType File > $null - Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'User' } -Verifiable - Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'Module' } -Verifiable -MockWith { + Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'User' } -Verifiable -ModuleName 'PSKoans' + Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'Module' } -Verifiable -ModuleName 'PSKoans' -MockWith { [PSCustomObject]@{ Topic = $Topic Module = '_powershell' @@ -116,28 +149,29 @@ Describe Reset-PSKoan { } } - Mock 'Update-PSKoan' + Mock 'Update-PSKoan' -ModuleName 'PSKoans' } It 'calls Update-PSKoan when the topic does not exist in the user location' { Reset-PSKoan -Topic DoesNotExist -ErrorAction Stop @defaultParams Should -InvokeVerifiable - Should -Invoke Update-PSKoan -Times 1 -Exactly + Should -Invoke Update-PSKoan -Times 1 -Exactly -ModuleName 'PSKoans' } } Context 'Module file does not exist' { BeforeAll { - Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'Module' } + Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'Module' } -ModuleName 'PSKoans' + Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'User' } -ModuleName 'PSKoans' } It 'throws a terminating error when no topics are found in the module' { { Reset-PSKoan -Topic DoesNotExist @defaultParams } | Should -Throw -ErrorId 'PSKoans.ModuleTopicNotFound,Reset-PSKoan' - Should -Invoke 'Get-PSKoan' -Times 1 -Exactly + Should -Invoke 'Get-PSKoan' -Times 1 -Exactly -ParameterFilter { $Scope -eq 'Module' } -ModuleName 'PSKoans' } } diff --git a/Tests/Functions/Public/Set-PSKoanLocation.Tests.ps1 b/Tests/Functions/Public/Set-PSKoanLocation.Tests.ps1 index cac34f907..bb2ba8949 100644 --- a/Tests/Functions/Public/Set-PSKoanLocation.Tests.ps1 +++ b/Tests/Functions/Public/Set-PSKoanLocation.Tests.ps1 @@ -1,9 +1,31 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Set-PSKoanLocation' { BeforeAll { - Mock 'Set-PSKoanSetting' -ParameterFilter { $Name -eq 'KoanLocation' } + Mock 'Set-PSKoanSetting' -ParameterFilter { $Name -eq 'KoanLocation' } -ModuleName 'PSKoans' } It 'outputs no data by default' { @@ -11,7 +33,7 @@ Describe 'Set-PSKoanLocation' { } It 'sets the KoanLocation setting' { - Should -Invoke 'Set-PSKoanSetting' -Scope Describe + Should -Invoke 'Set-PSKoanSetting' -Scope Describe -ModuleName 'PSKoans' } It 'returns the input -Path value back to the pipeline with -PassThru' { diff --git a/Tests/Functions/Public/Set-PSKoanSetting.Tests.ps1 b/Tests/Functions/Public/Set-PSKoanSetting.Tests.ps1 index 5feec692f..0b3645959 100644 --- a/Tests/Functions/Public/Set-PSKoanSetting.Tests.ps1 +++ b/Tests/Functions/Public/Set-PSKoanSetting.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Set-PSKoanSetting' { diff --git a/Tests/Functions/Public/Show-Advice.Tests.ps1 b/Tests/Functions/Public/Show-Advice.Tests.ps1 index e91fa726a..aa4713352 100644 --- a/Tests/Functions/Public/Show-Advice.Tests.ps1 +++ b/Tests/Functions/Public/Show-Advice.Tests.ps1 @@ -1,4 +1,26 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe "Show-Advice" { @@ -50,8 +72,8 @@ Describe "Show-Advice" { BeforeAll { $GetContentResult = [string]::Empty - Mock Get-Content -MockWith { $GetContentResult } -Verifiable - Mock Get-ChildItem -MockWith { [PSCustomObject]@{ PSPath = "DummyPath" } } -Verifiable + Mock Get-Content -MockWith { $GetContentResult } -Verifiable -ModuleName 'PSKoans' + Mock Get-ChildItem -MockWith { [PSCustomObject]@{ PSPath = "DummyPath" } } -Verifiable -ModuleName 'PSKoans' } It "should throw an error if the requested file's format is not correct" -TestCases @( diff --git a/Tests/Functions/Public/Show-Karma.Tests.ps1 b/Tests/Functions/Public/Show-Karma.Tests.ps1 index f0bd6fb8e..6de7409c6 100644 --- a/Tests/Functions/Public/Show-Karma.Tests.ps1 +++ b/Tests/Functions/Public/Show-Karma.Tests.ps1 @@ -1,11 +1,32 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Show-Karma' { BeforeAll { - Mock 'Get-PSKoanLocation' { - "$TestDrive/Koans" - } + $koanLocation = "$TestDrive/Koans" + Mock 'Get-PSKoanLocation' -ModuleName 'PSKoans' { $koanLocation } $EditorSetting = Get-PSKoanSetting -Name Editor @@ -19,8 +40,8 @@ Describe 'Show-Karma' { Context 'Default Behaviour' { BeforeAll { - Mock 'Out-Host' - Mock 'Get-Karma' { + Mock 'Out-Host' -ModuleName 'PSKoans' + Mock 'Get-Karma' -ModuleName 'PSKoans' { [PSCustomObject]@{ PSTypeName = 'PSKoans.Result' Meditation = 'TestMeditation' @@ -44,19 +65,19 @@ Describe 'Show-Karma' { } It 'should write the formatted output to host' { - Should -Invoke 'Out-Host' -Scope Context + Should -Invoke 'Out-Host' -Scope Context -ModuleName 'PSKoans' } It 'should call Get-Karma to examine the koans' { - Should -Invoke 'Get-Karma' -Scope Context + Should -Invoke 'Get-Karma' -Scope Context -ModuleName 'PSKoans' } } Context 'With All Koans Completed' { BeforeAll { - Mock 'Out-Host' -Verifiable - Mock 'Get-Karma' -Verifiable { + Mock 'Out-Host' -Verifiable -ModuleName 'PSKoans' + Mock 'Get-Karma' -Verifiable -ModuleName 'PSKoans' { [PSCustomObject]@{ PSTypeName = 'PSKoans.CompleteResult' KoansPassed = 10 @@ -76,9 +97,9 @@ Describe 'Show-Karma' { Context 'With -ClearScreen Switch' { BeforeAll { - Mock 'Clear-Host' - Mock 'Out-Host' - Mock 'Get-Karma' { + Mock 'Clear-Host' -ModuleName 'PSKoans' + Mock 'Out-Host' -ModuleName 'PSKoans' + Mock 'Get-Karma' -ModuleName 'PSKoans' { [PSCustomObject]@{ PSTypeName = 'PSKoans.Result' Meditation = 'TestMeditation' @@ -102,27 +123,27 @@ Describe 'Show-Karma' { } It 'should clear the screen' { - Should -Invoke 'Clear-Host' -Scope Context -Times 1 -Exactly + Should -Invoke 'Clear-Host' -Scope Context -Times 1 -Exactly -ModuleName 'PSKoans' } It 'should display the rendered output' { - Should -Invoke 'Out-Host' -Scope Context + Should -Invoke 'Out-Host' -Scope Context -ModuleName 'PSKoans' } It 'should use Get-Karma to retrieve koan results' { - Should -Invoke 'Get-Karma' -Scope Context -Times 1 -Exactly + Should -Invoke 'Get-Karma' -Scope Context -Times 1 -Exactly -ModuleName 'PSKoans' } } Context 'With Nonexistent Koans Folder / No Koans Found' { BeforeAll { - Mock 'Write-Host' - Mock 'Get-PSKoan' - Mock 'Update-PSKoan' { throw 'Prevent recursion' } - Mock 'Write-Warning' - Mock 'Test-Path' { $false } - Mock 'Invoke-Item' + Mock 'Write-Host' -ModuleName 'PSKoans' + Mock 'Get-PSKoan' -ModuleName 'PSKoans' + Mock 'Update-PSKoan' { throw 'Prevent recursion' } -ModuleName 'PSKoans' + Mock 'Write-Warning' -ModuleName 'PSKoans' + Mock 'Test-Path' { $false } -ModuleName 'PSKoans' + Mock 'Invoke-Item' -ModuleName 'PSKoans' Mock 'Measure-Koan' -ModuleName 'PSKoans' } @@ -135,7 +156,7 @@ Describe 'Show-Karma' { } It 'should display a warning before initiating a reset' { - Should -Invoke 'Write-Warning' -Scope Context -Times 1 -Exactly + Should -Invoke 'Write-Warning' -Scope Context -Times 1 -Exactly -ModuleName 'PSKoans' } It 'throws an error if a Topic is specified that matches nothing' { @@ -145,35 +166,35 @@ Describe 'Show-Karma' { It 'should create PSKoans directory with -Library' { { Show-Karma -Library } | Should -Throw -ExpectedMessage 'Prevent recursion' - Should -Invoke 'Test-Path' - Should -Invoke 'Update-PSKoan' -Times 1 -Exactly + Should -Invoke 'Test-Path' -ModuleName 'PSKoans' + Should -Invoke 'Update-PSKoan' -Times 1 -Exactly -ModuleName 'PSKoans' } It 'should call Get-PSKoan to retrieve the correct file -Contemplate' { { Show-Karma -Contemplate } | Should -Throw -ExpectedMessage 'Prevent recursion' - Should -Invoke 'Get-PSKoan' -Times 1 -Exactly - Should -Invoke 'Update-PSKoan' -Times 1 -Exactly + Should -Invoke 'Get-PSKoan' -Times 1 -Exactly -ModuleName 'PSKoans' + Should -Invoke 'Update-PSKoan' -Times 1 -Exactly -ModuleName 'PSKoans' } } Context 'With -ListTopics Parameter' { BeforeAll { - Mock 'Get-PSKoan' + Mock 'Get-PSKoan' -ModuleName 'PSKoans' } It 'should list all the koan topics' { Show-Karma -ListTopics - Should -Invoke 'Get-PSKoan' -Times 1 -Exactly + Should -Invoke 'Get-PSKoan' -Times 1 -Exactly -ModuleName 'PSKoans' } } Context 'With -Topic Parameter' { BeforeAll { - Mock 'Out-Host' -Verifiable - Mock 'Get-Karma' -ParameterFilter { $Topic -eq 'TestTopic' } -Verifiable -MockWith { + Mock 'Out-Host' -Verifiable -ModuleName 'PSKoans' + Mock 'Get-Karma' -ParameterFilter { $Topic -eq 'TestTopic' } -Verifiable -ModuleName 'PSKoans' -MockWith { [PSCustomObject]@{ PSTypeName = 'PSKoans.Result' Meditation = 'TestMeditation' @@ -202,9 +223,9 @@ Describe 'Show-Karma' { Context 'With All Koans in a Single Topic Completed' { BeforeAll { - Mock 'Format-Custom' -Verifiable { $null } - Mock 'Out-Host' -Verifiable - Mock 'Get-Karma' -Verifiable { + Mock 'Format-Custom' -Verifiable -ModuleName 'PSKoans' { $null } + Mock 'Out-Host' -Verifiable -ModuleName 'PSKoans' + Mock 'Get-Karma' -Verifiable -ModuleName 'PSKoans' { [PSCustomObject]@{ PSTypeName = 'PSKoans.CompleteResult' KoansPassed = 10 @@ -226,14 +247,14 @@ Describe 'Show-Karma' { BeforeAll { $TestFile = New-TemporaryFile - Mock 'Invoke-Item' { $Path } - Mock 'Get-Command' { $true } -ParameterFilter { $Name -ne "missing_editor" } - Mock 'Get-Command' { $false } -ParameterFilter { $Name -eq "missing_editor" } - Mock 'Start-Process' { + Mock 'Invoke-Item' { $Path } -ModuleName 'PSKoans' + Mock 'Get-Command' { $true } -ParameterFilter { $Name -ne "missing_editor" } -ModuleName 'PSKoans' + Mock 'Get-Command' { $false } -ParameterFilter { $Name -eq "missing_editor" } -ModuleName 'PSKoans' + Mock 'Start-Process' -ModuleName 'PSKoans' { @{ Editor = $FilePath; Arguments = $ArgumentList; NoNewWindow = $NoNewWindow } } - Mock 'Get-Karma' { + Mock 'Get-Karma' -ModuleName 'PSKoans' { $currentTopic = @{ Name = 'TestTopic' Completed = 0 @@ -258,7 +279,7 @@ Describe 'Show-Karma' { } } - Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'User' } { + Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'User' } -ModuleName 'PSKoans' { [PSCustomObject]@{ Path = $TestFile.FullName } } } @@ -281,8 +302,8 @@ Describe 'Show-Karma' { $Path = ($Result.Arguments[1] -split '(?<="):')[0] -replace '"' $Path | Should -BeExactly (Resolve-Path -Path $Path).Path - Should -Invoke 'Get-Command' -Times 1 -Exactly - Should -Invoke 'Start-Process' -Times 1 -Exactly + Should -Invoke 'Get-Command' -Times 1 -Exactly -ModuleName 'PSKoans' + Should -Invoke 'Start-Process' -Times 1 -Exactly -ModuleName 'PSKoans' InModuleScope 'PSKoans' { $script:CurrentTopic } | Should -BeNullOrEmpty } @@ -306,10 +327,10 @@ Describe 'Show-Karma' { $Path = ($Result.Arguments[1] -split '(?<="):')[0] -replace '"' $Path | Should -BeExactly (Resolve-Path -Path $Path).Path - Should -Invoke 'Get-Command' -Times 1 -Exactly - Should -Invoke 'Start-Process' -Times 1 -Exactly - Should -Invoke 'Get-Karma' -ParameterFilter { $Module -eq $ModuleName } - Should -Invoke 'Get-PSKoan' -ParameterFilter { $IncludeModule -eq $ModuleName } + Should -Invoke 'Get-Command' -Times 1 -Exactly -ModuleName 'PSKoans' + Should -Invoke 'Start-Process' -Times 1 -Exactly -ModuleName 'PSKoans' + Should -Invoke 'Get-Karma' -ParameterFilter { $Module -eq $ModuleName } -ModuleName 'PSKoans' + Should -Invoke 'Get-PSKoan' -ParameterFilter { $IncludeModule -eq $ModuleName } -ModuleName 'PSKoans' InModuleScope 'PSKoans' { $script:CurrentTopic } | Should -BeNullOrEmpty } @@ -320,8 +341,8 @@ Describe 'Show-Karma' { $Result = Show-Karma -Contemplate -Topic TestTopic $Result.Arguments[1] | Should -MatchExactly ([regex]::Escape($TestFile.FullName)) - Should -Invoke 'Get-Command' -Times 1 -Exactly - Should -Invoke 'Start-Process' -Times 1 -Exactly + Should -Invoke 'Get-Command' -Times 1 -Exactly -ModuleName 'PSKoans' + Should -Invoke 'Start-Process' -Times 1 -Exactly -ModuleName 'PSKoans' InModuleScope 'PSKoans' { $script:CurrentTopic } | Should -BeNullOrEmpty } @@ -337,8 +358,8 @@ Describe 'Show-Karma' { $Path = $Result.Arguments -replace '"' $Path | Should -BeExactly (Resolve-Path -Path $Path).Path - Should -Invoke 'Get-Command' -Times 1 -Exactly - Should -Invoke 'Start-Process' -Times 1 -Exactly + Should -Invoke 'Get-Command' -Times 1 -Exactly -ModuleName 'PSKoans' + Should -Invoke 'Start-Process' -Times 1 -Exactly -ModuleName 'PSKoans' InModuleScope 'PSKoans' { $script:CurrentTopic } | Should -BeNullOrEmpty } @@ -348,8 +369,8 @@ Describe 'Show-Karma' { Show-Karma -Contemplate | Should -BeExactly $TestFile.FullName - Should -Invoke 'Get-Command' -Times 1 -Exactly -ParameterFilter { $Name -eq "missing_editor" } - Should -Invoke 'Invoke-Item' -Times 1 -Exactly + Should -Invoke 'Get-Command' -Times 1 -Exactly -ParameterFilter { $Name -eq "missing_editor" } -ModuleName 'PSKoans' + Should -Invoke 'Invoke-Item' -Times 1 -Exactly -ModuleName 'PSKoans' InModuleScope 'PSKoans' { $script:CurrentTopic } | Should -BeNullOrEmpty } @@ -358,12 +379,12 @@ Describe 'Show-Karma' { Context 'With -Library Switch' { BeforeAll { - Mock 'Get-Command' { $true } -ParameterFilter { $Name -ne "missing_editor" } - Mock 'Get-Command' { $false } -ParameterFilter { $Name -eq "missing_editor" } - Mock 'Start-Process' { + Mock 'Get-Command' { $true } -ParameterFilter { $Name -ne "missing_editor" } -ModuleName 'PSKoans' + Mock 'Get-Command' { $false } -ParameterFilter { $Name -eq "missing_editor" } -ModuleName 'PSKoans' + Mock 'Start-Process' -ModuleName 'PSKoans' { @{ Editor = $FilePath; Arguments = $ArgumentList } } - Mock 'Invoke-Item' { $Path } + Mock 'Invoke-Item' { $Path } -ModuleName 'PSKoans' } It 'invokes VS Code with "code" set as Editor with proper arguments' { @@ -376,8 +397,8 @@ Describe 'Show-Karma' { $Path = $Result.Arguments -replace '"' $Path | Should -BeExactly (Resolve-Path -Path $Path).Path - Should -Invoke 'Get-Command' -Times 1 -Exactly - Should -Invoke 'Start-Process' -Times 1 -Exactly + Should -Invoke 'Get-Command' -Times 1 -Exactly -ModuleName 'PSKoans' + Should -Invoke 'Start-Process' -Times 1 -Exactly -ModuleName 'PSKoans' } It 'invokes the set editor with unknown editor chosen' { @@ -390,17 +411,17 @@ Describe 'Show-Karma' { $Path = $Result.Arguments -replace '"' $Path | Should -BeExactly (Resolve-Path -Path $Path).Path - Should -Invoke 'Get-Command' -Times 1 -Exactly - Should -Invoke 'Start-Process' -Times 1 -Exactly + Should -Invoke 'Get-Command' -Times 1 -Exactly -ModuleName 'PSKoans' + Should -Invoke 'Start-Process' -Times 1 -Exactly -ModuleName 'PSKoans' } It 'opens the file directly when selected editor is unavailable' { Set-PSKoanSetting -name Editor -Value "missing_editor" - Show-Karma -Library | Should -BeExactly (Get-PSKoanLocation) + Show-Karma -Library | Should -BeExactly $koanLocation - Should -Invoke 'Get-Command' -Times 1 -Exactly -ParameterFilter { $Name -eq "missing_editor" } - Should -Invoke 'Invoke-Item' -Times 1 -Exactly + Should -Invoke 'Get-Command' -Times 1 -Exactly -ParameterFilter { $Name -eq "missing_editor" } -ModuleName 'PSKoans' + Should -Invoke 'Invoke-Item' -Times 1 -Exactly -ModuleName 'PSKoans' } } } diff --git a/Tests/Functions/Public/Update-PSKoan.Tests.ps1 b/Tests/Functions/Public/Update-PSKoan.Tests.ps1 index d86cf7f29..921dd396d 100644 --- a/Tests/Functions/Public/Update-PSKoan.Tests.ps1 +++ b/Tests/Functions/Public/Update-PSKoan.Tests.ps1 @@ -1,17 +1,39 @@ -#Requires -Modules PSKoans +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} Describe 'Update-PSKoan' { Context 'Mocked Commands' { BeforeAll { - Mock 'Remove-Item' - Mock 'Copy-Item' - Mock 'New-Item' - Mock 'Move-Item' + Mock 'Remove-Item' -ModuleName 'PSKoans' + Mock 'Copy-Item' -ModuleName 'PSKoans' + Mock 'New-Item' -ModuleName 'PSKoans' + Mock 'Move-Item' -ModuleName 'PSKoans' Mock 'Update-PSKoanFile' -ModuleName 'PSKoans' - Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'Module' } -MockWith { + Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'Module' } -ModuleName 'PSKoans' -MockWith { [PSCustomObject]@{ Topic = 'Missing' Path = 'Module\Group\AboutSomethingMissing.Koans.ps1' @@ -26,7 +48,7 @@ Describe 'Update-PSKoan' { } } - Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'User' } -MockWith { + Mock 'Get-PSKoan' -ParameterFilter { $Scope -eq 'User' } -ModuleName 'PSKoans' -MockWith { [PSCustomObject]@{ Topic = 'IncorrectPath' Path = 'Module\RetiredGroup\AboutSomethingIncorrectPath.Koans.ps1' @@ -47,15 +69,15 @@ Describe 'Update-PSKoan' { } It 'should copy missing topic files' { - Should -Invoke 'Copy-Item' -Times 1 -Scope Context + Should -Invoke 'Copy-Item' -Times 1 -Scope Context -ModuleName 'PSKoans' } It 'should move incorrectly placed topics' { - Should -Invoke 'Remove-Item' -Times 1 -Scope Context + Should -Invoke 'Remove-Item' -Times 1 -Scope Context -ModuleName 'PSKoans' } It 'should remove discarded topics' { - Should -Invoke 'Remove-Item' -Times 1 -Scope Context + Should -Invoke 'Remove-Item' -Times 1 -Scope Context -ModuleName 'PSKoans' } It 'should update topics which exist in module and koan path' { @@ -66,14 +88,13 @@ Describe 'Update-PSKoan' { Context 'Practical Tests with TestDrive' { BeforeAll { - Mock 'Get-PSKoanLocation' { - Join-Path -Path $TestDrive -ChildPath 'PSKoans' - } + $koanLocation = Join-Path -Path $TestDrive -ChildPath 'PSKoans' + Mock 'Get-PSKoanLocation' -ModuleName 'PSKoans' { $koanLocation } - New-Item -Path (Get-PSKoanLocation) -ItemType Directory + New-Item -Path $koanLocation -ItemType Directory Update-PSKoan -Confirm:$false - $file = Get-ChildItem -Path (Get-PSKoanLocation) -Filter *.koans.ps1 -File -Recurse | + $file = Get-ChildItem -Path $koanLocation -Filter *.koans.ps1 -File -Recurse | Select-Object -First 1 } diff --git a/Tests/KoanValidation.Tests.ps1 b/Tests/KoanValidation.Tests.ps1 index ac6b8e6c3..44f80ce18 100644 --- a/Tests/KoanValidation.Tests.ps1 +++ b/Tests/KoanValidation.Tests.ps1 @@ -1,8 +1,30 @@ -#Requires -Modules PSKoans - using namespace System.Management.Automation.Language using namespace System.Collections.Generic +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} + Describe 'Static Analysis: Koan Topics' { Context 'Individual Topics' { diff --git a/Tests/ModuleHelp.Tests.ps1 b/Tests/ModuleHelp.Tests.ps1 index 18141f0f4..c22ac27f1 100644 --- a/Tests/ModuleHelp.Tests.ps1 +++ b/Tests/ModuleHelp.Tests.ps1 @@ -1,20 +1,33 @@ -#region Discovery +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} $ModuleName = 'PSKoans' -#endregion Discovery - -BeforeAll { - $ModuleName = 'PSKoans' - Import-Module $ModuleName -} - Describe "$ModuleName Sanity Tests - Help Content" -Tags 'Module' { #region Discovery - # The module will need to be imported during Discovery since we're using it to generate test cases / Context blocks - Import-Module $ModuleName $ShouldProcessParameters = 'WhatIf', 'Confirm' @@ -72,16 +85,20 @@ Describe "$ModuleName Sanity Tests - Help Content" -Tags 'Module' { @($Parameters).Count | Should -Be $Ast.Body.ParamBlock.Parameters.Count -Because 'the number of parameters in the help should match the number in the function script' } - It "has a description for $Command parameter -" -TestCases $Parameters -Skip:(-not $Parameters) { - $Description | Should -Not -BeNullOrEmpty -Because "parameter $Name should have a description" + if ($Parameters) { + It "has a description for $Command parameter -" -TestCases $Parameters { + $Description | Should -Not -BeNullOrEmpty -Because "parameter $Name should have a description" + } } It "has at least one usage example for $Command" -TestCases $Help { $Help.Examples.Example.Code.Count | Should -BeGreaterOrEqual 1 } - It "lists a description for $Command example: " -TestCases $Examples { - $Example.Remarks | Should -Not -BeNullOrEmpty -Because "example $($Example.Title) should have a description!" + if ($Examples) { + It "lists a description for $Command example: <Title>" -TestCases $Examples { + $Example.Remarks | Should -Not -BeNullOrEmpty -Because "example $($Example.Title) should have a description!" + } } } } diff --git a/Tests/ModuleValidation.Tests.ps1 b/Tests/ModuleValidation.Tests.ps1 index b13f7c992..bc26f8d1c 100644 --- a/Tests/ModuleValidation.Tests.ps1 +++ b/Tests/ModuleValidation.Tests.ps1 @@ -1,3 +1,27 @@ +#Requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } + +BeforeDiscovery { + if ($null -eq $env:BHProjectName) { + # Run the build in a child process -- build.ps1 calls `exit` on completion, which + # would otherwise terminate the host process running this test file. + & pwsh -NoProfile -File '.\build.ps1' -Task Build + if ($LASTEXITCODE -ne 0) { + throw 'Failed to build PSKoans before running tests.' + } + Set-BuildEnvironment -Force + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + $env:PSModulePath = $outputModDir + [IO.Path]::PathSeparator + $env:PSModulePath + + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} + Describe 'Static Analysis: Module & Repository Files' { #region Discovery diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 000000000..55f87b50c --- /dev/null +++ b/build.ps1 @@ -0,0 +1,68 @@ +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSReviewUnusedParameter', + 'Command', + Justification = 'false positive' +)] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSReviewUnusedParameter', + 'Parameter', + Justification = 'false positive' +)] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSReviewUnusedParameter', + 'CommandAst', + Justification = 'false positive' +)] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSReviewUnusedParameter', + 'FakeBoundParams', + Justification = 'false positive' +)] +[CmdletBinding(DefaultParameterSetName = 'task')] +param( + [parameter(ParameterSetName = 'task', Position = 0)] + [ArgumentCompleter( { + param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) + try { + Get-PSakeScriptTasks -BuildFile (Join-Path $PSScriptRoot 'psakeFile.ps1') -ErrorAction 'Stop' | + Where-Object { $_.Name -like "$WordToComplete*" } | + Select-Object -ExpandProperty 'Name' + } + catch { + @() + } + })] + [string[]]$Task = 'default', + [switch]$Bootstrap, + [parameter(ParameterSetName = 'Help')] + [switch]$Help +) + +$ErrorActionPreference = 'Stop' +$requirementsFile = Join-Path $PSScriptRoot 'requirements.psd1' +$psakeFile = Join-Path $PSScriptRoot 'psakeFile.ps1' + +if ($Bootstrap) { + if (-not (Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue)) { + Install-PackageProvider -Name NuGet -Force -Scope CurrentUser | Out-Null + } + Set-PSRepository -Name PSGallery -InstallationPolicy Trusted + if (-not (Get-Module -Name PSDepend -ListAvailable)) { + Install-Module -Name PSDepend -Repository PSGallery -Scope CurrentUser -Force -RequiredVersion '0.3.8' + } + Import-Module -Name PSDepend -Verbose:$false + Invoke-PSDepend -Path $requirementsFile -Install -Import -Force -WarningAction SilentlyContinue +} +else { + Invoke-PSDepend -Path $requirementsFile -Import -Force -WarningAction SilentlyContinue +} + +if ($PSCmdlet.ParameterSetName -eq 'Help') { + Get-PSakeScriptTasks -BuildFile $psakeFile | + Format-Table -Property Name, Description, Alias, DependsOn +} +else { + Set-BuildEnvironment -Force + Invoke-Psake -BuildFile $psakeFile -TaskList $Task -NoLogo + exit ([int](-not $psake.build_success)) +} diff --git a/psakeFile.ps1 b/psakeFile.ps1 new file mode 100644 index 000000000..4d5fee156 --- /dev/null +++ b/psakeFile.ps1 @@ -0,0 +1,46 @@ +Properties { + # PSKoans stages files without compiling to a single PSM1 + $PSBPreference.Build.CompileModule = $false + + # Help generation + $PSBPreference.Help.DefaultLocale = 'en-US' + + # Test configuration -- the module must be imported from the staged output before + # Pester runs since the test suite expects `PSKoans` to already be loaded/resolvable + $PSBPreference.Test.RootDir = Join-Path $PSScriptRoot 'Tests' + $PSBPreference.Test.ImportModule = $true + $PSBPreference.Test.OutputFile = 'out/testResults.xml' + $PSBPreference.Test.OutputFormat = 'JUnitXml' + $PSBPreference.Test.ScriptAnalysis.Enabled = $true + $PSBPreference.Test.ScriptAnalysis.FailBuildOnSeverityLevel = 'Error' + $PSBPreference.Test.CodeCoverage.Enabled = $false + + $PSBPreference.Publish.PSRepositoryApiKey = $env:PSGALLERY_API_KEY +} + +# The module manifest's FormatsToProcess entry (PSKoans.format.ps1xml) is generated +# from ./formatting via EZOut and is gitignored -- it must exist before the module +# can be imported at all, so generate it ahead of staging. +Task GenerateFormatData -Depends Clean { + & (Join-Path $PSBPreference.General.ProjectRoot 'PSKoans.ezformat.ps1') +} -Description 'Generates PSKoans.format.ps1xml from ./formatting' + +$PSBStageFilesDependency = @('Clean', 'GenerateFormatData') +$PSBBuildDependency = @('StageFiles') + +Task Default -Depends Test + +# PowerShellBuild adds the following tasks: +# - Init +# - Clean +# - StageFiles +# - Build +# - Analyze +# - Pester +# - Test +# - BuildHelp +# - GenerateMarkdown +# - GenerateMAML +# - GenerateUpdatableHelp +# - Publish +Task Test -FromModule PowerShellBuild -MinimumVersion '0.7.3' diff --git a/requirements.psd1 b/requirements.psd1 new file mode 100644 index 000000000..e8741d4e5 --- /dev/null +++ b/requirements.psd1 @@ -0,0 +1,25 @@ +@{ + PSDependOptions = @{ + Target = 'CurrentUser' + } + 'psake' = @{ + Version = '4.9.1' + } + 'PowerShellBuild' = @{ + Version = '0.7.3' + } + 'Pester' = @{ + Parameters = @{ + SkipPublisherCheck = $true + } + } + 'PSScriptAnalyzer' = @{ + Version = '1.19.1' + } + 'BuildHelpers' = @{ + Version = '2.0.16' + } + 'EZOut' = @{ + Version = '2.0.6' + } +}