1+ <#
2+ . NOTES
3+ The original test this is based upon was written by June Blender.
4+ After several rounds of modifications it stands now as it is, but the honor remains hers.
5+
6+ Thank you June, for all you have done!
7+
8+ . DESCRIPTION
9+ This test evaluates the help for all commands in a module.
10+
11+ . PARAMETER SkipTest
12+ Disables this test.
13+
14+ . PARAMETER CommandPath
15+ List of paths under which the script files are stored.
16+ This test assumes that all functions have their own file that is named after themselves.
17+ These paths are used to search for commands that should exist and be tested.
18+ Will search recursively and accepts wildcards, make sure only functions are found
19+
20+ . PARAMETER ModuleName
21+ Name of the module to be tested.
22+ The module must already be imported
23+
24+ . PARAMETER ExceptionsFile
25+ File in which exceptions and adjustments are configured.
26+ In it there should be two arrays and a hashtable defined:
27+ $global:FunctionHelpTestExceptions
28+ $global:HelpTestEnumeratedArrays
29+ $global:HelpTestSkipParameterType
30+ These can be used to tweak the tests slightly in cases of need.
31+ See the example file for explanations on each of these usage and effect.
32+ #>
33+ [CmdletBinding ()]
34+ Param (
35+ [switch ]
36+ $SkipTest ,
37+
38+ [string []]
39+ $CommandPath = @ (" $PSScriptRoot \..\..\functions" , " $PSScriptRoot \..\..\internal\functions" ),
40+
41+ [string ]
42+ $ModuleName = " PSModuleDevelopment" ,
43+
44+ [string ]
45+ $ExceptionsFile = " $PSScriptRoot \Help.Exceptions.ps1"
46+ )
47+ if ($SkipTest ) { return }
48+ . $ExceptionsFile
49+
50+ $includedNames = (Get-ChildItem $CommandPath - Recurse - File | Where-Object Name -like " *.ps1" ).BaseName
51+ $commands = Get-Command - Module (Get-Module $ModuleName ) - CommandType Cmdlet, Function, Workflow | Where-Object Name -in $includedNames
52+
53+ # # When testing help, remember that help is cached at the beginning of each session.
54+ # # To test, restart session.
55+
56+
57+ foreach ($command in $commands ) {
58+ $commandName = $command.Name
59+
60+ # Skip all functions that are on the exclusions list
61+ if ($global :FunctionHelpTestExceptions -contains $commandName ) { continue }
62+
63+ # The module-qualified command fails on Microsoft.PowerShell.Archive cmdlets
64+ $Help = Get-Help $commandName - ErrorAction SilentlyContinue
65+ $testhelperrors = 0
66+ $testhelpall = 0
67+ Describe " Test help for $commandName " {
68+
69+ $testhelpall += 1
70+ if ($Help.Synopsis -like ' *`[`<CommonParameters`>`]*' ) {
71+ # If help is not found, synopsis in auto-generated help is the syntax diagram
72+ It " should not be auto-generated" {
73+ $Help.Synopsis | Should Not BeLike ' *`[`<CommonParameters`>`]*'
74+ }
75+ $testhelperrors += 1
76+ }
77+
78+ $testhelpall += 1
79+ if ([String ]::IsNullOrEmpty($Help.Description.Text )) {
80+ # Should be a description for every function
81+ It " gets description for $commandName " {
82+ $Help.Description | Should Not BeNullOrEmpty
83+ }
84+ $testhelperrors += 1
85+ }
86+
87+ $testhelpall += 1
88+ if ([String ]::IsNullOrEmpty(($Help.Examples.Example | Select-Object - First 1 ).Code)) {
89+ # Should be at least one example
90+ It " gets example code from $commandName " {
91+ ($Help.Examples.Example | Select-Object - First 1 ).Code | Should Not BeNullOrEmpty
92+ }
93+ $testhelperrors += 1
94+ }
95+
96+ $testhelpall += 1
97+ if ([String ]::IsNullOrEmpty(($Help.Examples.Example.Remarks | Select-Object - First 1 ).Text)) {
98+ # Should be at least one example description
99+ It " gets example help from $commandName " {
100+ ($Help.Examples.Example.Remarks | Select-Object - First 1 ).Text | Should Not BeNullOrEmpty
101+ }
102+ $testhelperrors += 1
103+ }
104+
105+ if ($testhelperrors -eq 0 ) {
106+ It " Ran silently $testhelpall tests" {
107+ $testhelperrors | Should be 0
108+ }
109+ }
110+
111+ $testparamsall = 0
112+ $testparamserrors = 0
113+ Context " Test parameter help for $commandName " {
114+
115+ $Common = ' Debug' , ' ErrorAction' , ' ErrorVariable' , ' InformationAction' , ' InformationVariable' , ' OutBuffer' , ' OutVariable' ,
116+ ' PipelineVariable' , ' Verbose' , ' WarningAction' , ' WarningVariable'
117+
118+ $parameters = $command.ParameterSets.Parameters | Sort-Object - Property Name - Unique | Where-Object Name -notin $common
119+ $parameterNames = $parameters.Name
120+ $HelpParameterNames = $Help.Parameters.Parameter.Name | Sort-Object - Unique
121+ foreach ($parameter in $parameters ) {
122+ $parameterName = $parameter.Name
123+ $parameterHelp = $Help.parameters.parameter | Where-Object Name -EQ $parameterName
124+
125+ $testparamsall += 1
126+ if ([String ]::IsNullOrEmpty($parameterHelp.Description.Text )) {
127+ # Should be a description for every parameter
128+ It " gets help for parameter: $parameterName : in $commandName " {
129+ $parameterHelp.Description.Text | Should Not BeNullOrEmpty
130+ }
131+ $testparamserrors += 1
132+ }
133+
134+ $testparamsall += 1
135+ $codeMandatory = $parameter.IsMandatory.toString ()
136+ if ($parameterHelp.Required -ne $codeMandatory ) {
137+ # Required value in Help should match IsMandatory property of parameter
138+ It " help for $parameterName parameter in $commandName has correct Mandatory value" {
139+ $parameterHelp.Required | Should Be $codeMandatory
140+ }
141+ $testparamserrors += 1
142+ }
143+
144+ if ($HelpTestSkipParameterType [$commandName ] -contains $parameterName ) { continue }
145+
146+ $codeType = $parameter.ParameterType.Name
147+
148+ $testparamsall += 1
149+ if ($parameter.ParameterType.IsEnum ) {
150+ # Enumerations often have issues with the typename not being reliably available
151+ $names = $parameter.ParameterType ::GetNames($parameter.ParameterType )
152+ if ($parameterHelp.parameterValueGroup.parameterValue -ne $names ) {
153+ # Parameter type in Help should match code
154+ It " help for $commandName has correct parameter type for $parameterName " {
155+ $parameterHelp.parameterValueGroup.parameterValue | Should be $names
156+ }
157+ $testparamserrors += 1
158+ }
159+ }
160+ elseif ($parameter.ParameterType.FullName -in $HelpTestEnumeratedArrays ) {
161+ # Enumerations often have issues with the typename not being reliably available
162+ $names = [Enum ]::GetNames($parameter.ParameterType.DeclaredMembers [0 ].ReturnType)
163+ if ($parameterHelp.parameterValueGroup.parameterValue -ne $names ) {
164+ # Parameter type in Help should match code
165+ It " help for $commandName has correct parameter type for $parameterName " {
166+ $parameterHelp.parameterValueGroup.parameterValue | Should be $names
167+ }
168+ $testparamserrors += 1
169+ }
170+ }
171+ else {
172+ # To avoid calling Trim method on a null object.
173+ $helpType = if ($parameterHelp.parameterValue ) { $parameterHelp.parameterValue.Trim () }
174+ if ($helpType -ne $codeType ) {
175+ # Parameter type in Help should match code
176+ It " help for $commandName has correct parameter type for $parameterName " {
177+ $helpType | Should be $codeType
178+ }
179+ $testparamserrors += 1
180+ }
181+ }
182+ }
183+ foreach ($helpParm in $HelpParameterNames ) {
184+ $testparamsall += 1
185+ if ($helpParm -notin $parameterNames ) {
186+ # Shouldn't find extra parameters in help.
187+ It " finds help parameter in code: $helpParm " {
188+ $helpParm -in $parameterNames | Should Be $true
189+ }
190+ $testparamserrors += 1
191+ }
192+ }
193+ if ($testparamserrors -eq 0 ) {
194+ It " Ran silently $testparamsall tests" {
195+ $testparamserrors | Should be 0
196+ }
197+ }
198+ }
199+ }
200+ }
0 commit comments