Skip to content

Commit 9b9fe53

Browse files
committed
test: ✨ Add tests from Stucco
* Introduced `Help.tests.ps1` to validate command help and parameters.
1 parent 88eadb7 commit 9b9fe53

5 files changed

Lines changed: 344 additions & 1 deletion

File tree

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@
1313
"powershell.scriptAnalysis.settingsPath": "ScriptAnalyzerSettings.psd1",
1414
//----------Code Formatting ----------------
1515
"powershell.codeFormatting.preset": "OTBS",
16-
"editor.formatOnSave": true
16+
"editor.formatOnSave": true,
17+
"powershell.scriptAnalysis.enable": true
1718
}

tests/Help.tests.ps1

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Taken with love from @juneb_get_help (https://raw.githubusercontent.com/juneb/PesterTDD/master/Module.Help.Tests.ps1)
2+
3+
BeforeDiscovery {
4+
function global:FilterOutCommonParams {
5+
param ($Params)
6+
$commonParameters = [System.Management.Automation.PSCmdlet]::CommonParameters +
7+
[System.Management.Automation.PSCmdlet]::OptionalCommonParameters
8+
$params | Where-Object { $_.Name -notin $commonParameters } | Sort-Object -Property Name -Unique
9+
}
10+
11+
$manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest
12+
$outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output'
13+
$outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName
14+
$outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion
15+
$outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1"
16+
17+
# Get module commands
18+
# Remove all versions of the module from the session. Pester can't handle multiple versions.
19+
Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore
20+
Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop
21+
$params = @{
22+
Module = (Get-Module $env:BHProjectName)
23+
CommandType = [System.Management.Automation.CommandTypes[]]'Cmdlet, Function' # Not alias
24+
}
25+
if ($PSVersionTable.PSVersion.Major -lt 6) {
26+
$params.CommandType[0] += 'Workflow'
27+
}
28+
$commands = Get-Command @params
29+
$global:customEnumTypes = @(
30+
# Add custom enums here
31+
)
32+
33+
## When testing help, remember that help is cached at the beginning of each session.
34+
## To test, restart session.
35+
}
36+
37+
Describe "Test help for <_.Name>" -ForEach $commands {
38+
39+
BeforeDiscovery {
40+
# Get command help, parameters, and links
41+
$command = $_
42+
$commandHelp = Get-Help $command.Name -ErrorAction SilentlyContinue
43+
$commandParameters = global:FilterOutCommonParams -Params $command.ParameterSets.Parameters
44+
$commandParameterNames = $commandParameters.Name
45+
$helpLinks = $commandHelp.relatedLinks.navigationLink.uri
46+
}
47+
48+
BeforeAll {
49+
# These vars are needed in both discovery and test phases so we need to duplicate them here
50+
$command = $_
51+
$commandName = $_.Name
52+
$commandHelp = Get-Help $command.Name -ErrorAction SilentlyContinue
53+
$commandParameters = global:FilterOutCommonParams -Params $command.ParameterSets.Parameters
54+
$commandParameterNames = $commandParameters.Name
55+
$helpParameters = global:FilterOutCommonParams -Params $commandHelp.Parameters.Parameter
56+
$helpParameterNames = $helpParameters.Name
57+
}
58+
59+
# If help is not found, synopsis in auto-generated help is the syntax diagram
60+
It 'Help is not auto-generated' {
61+
$commandHelp.Synopsis | Should -Not -BeLike '*`[`<CommonParameters`>`]*'
62+
}
63+
64+
# Should be a description for every function
65+
It "Has description" {
66+
$commandHelp.Description | Should -Not -BeNullOrEmpty
67+
}
68+
69+
# Should be at least one example
70+
It "Has example code" {
71+
($commandHelp.Examples.Example | Select-Object -First 1).Code | Should -Not -BeNullOrEmpty
72+
}
73+
74+
# Should be at least one example description
75+
It "Has example help" {
76+
($commandHelp.Examples.Example.Remarks | Select-Object -First 1).Text | Should -Not -BeNullOrEmpty
77+
}
78+
79+
It "Help link <_> is valid" -ForEach $helpLinks {
80+
(Invoke-WebRequest -Uri $_ -UseBasicParsing).StatusCode | Should -Be '200'
81+
}
82+
83+
Context "Parameter <_.Name>" -ForEach $commandParameters {
84+
85+
BeforeAll {
86+
$parameter = $_
87+
$parameterName = $parameter.Name
88+
$parameterHelp = $commandHelp.parameters.parameter | Where-Object Name -EQ $parameterName
89+
$parameterHelpType = if ($parameterHelp.ParameterValue) { $parameterHelp.ParameterValue.Trim() }
90+
}
91+
92+
# Should be a description for every parameter
93+
It "Has description" {
94+
$parameterHelp.Description.Text | Should -Not -BeNullOrEmpty
95+
}
96+
97+
# Required value in Help should match IsMandatory property of parameter
98+
It "Has correct [mandatory] value" {
99+
$codeMandatory = $_.IsMandatory.toString()
100+
$parameterHelp.Required | Should -Be $codeMandatory
101+
}
102+
103+
# Parameter type in help should match code
104+
It "Has correct parameter type" {
105+
# If it's a custom object it won't show up in the help, so we skip it.
106+
if ($parameter.ParameterType -in $global:customEnumTypes) {
107+
Set-ItResult -Skipped -Because 'Custom object types are not shown in help.'
108+
}
109+
110+
$parameterHelpType | Should -Be $parameter.ParameterType.Name
111+
}
112+
}
113+
114+
Context "Test <_> help parameter help for <commandName>" -ForEach $helpParameterNames {
115+
116+
# Shouldn't find extra parameters in help.
117+
It "finds help parameter in code: <_>" {
118+
$_ -in $parameterNames | Should -Be $true
119+
}
120+
}
121+
}

tests/Manifest.tests.ps1

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
BeforeAll {
2+
3+
# NEW: Pre-Specify RegEx Matching Patterns
4+
$gitTagMatchRegEx = 'tag:\s?.(\d+(\.\d+)*)' # NOTE - was 'tag:\s*(\d+(?:\.\d+)*)' previously
5+
$changelogTagMatchRegEx = "^##\s\[(?<Version>(\d+\.){1,3}\d+)\]"
6+
7+
$moduleName = $env:BHProjectName
8+
$manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest
9+
$outputDir = Join-Path -Path $ENV:BHProjectPath -ChildPath 'Output'
10+
$outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName
11+
$outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion
12+
$outputManifestPath = Join-Path -Path $outputModVerDir -Child "$($moduleName).psd1"
13+
$manifestData = Test-ModuleManifest -Path $outputManifestPath -Verbose:$false -ErrorAction Stop -WarningAction SilentlyContinue
14+
15+
$changelogPath = Join-Path -Path $env:BHProjectPath -Child 'CHANGELOG.md'
16+
$changelogVersion = Get-Content $changelogPath | ForEach-Object {
17+
if ($_ -match $changelogTagMatchRegEx) {
18+
$changelogVersion = $matches.Version
19+
break
20+
}
21+
}
22+
23+
$script:manifest = $null
24+
}
25+
Describe 'Module manifest' {
26+
27+
Context 'Validation' {
28+
29+
It 'Has a valid manifest' {
30+
$manifestData | Should -Not -BeNullOrEmpty
31+
}
32+
33+
It 'Has a valid name in the manifest' {
34+
$manifestData.Name | Should -Be $moduleName
35+
}
36+
37+
It 'Has a valid root module' {
38+
$manifestData.RootModule | Should -Be "$($moduleName).psm1"
39+
}
40+
41+
It 'Has a valid version in the manifest' {
42+
$manifestData.Version -as [Version] | Should -Not -BeNullOrEmpty
43+
}
44+
45+
It 'Has a valid description' {
46+
$manifestData.Description | Should -Not -BeNullOrEmpty
47+
}
48+
49+
It 'Has a valid author' {
50+
$manifestData.Author | Should -Not -BeNullOrEmpty
51+
}
52+
53+
It 'Has a valid guid' {
54+
{[guid]::Parse($manifestData.Guid)} | Should -Not -Throw
55+
}
56+
57+
It 'Has a valid copyright' {
58+
$manifestData.CopyRight | Should -Not -BeNullOrEmpty
59+
}
60+
61+
It 'Has a valid version in the changelog' {
62+
$changelogVersion | Should -Not -BeNullOrEmpty
63+
$changelogVersion -as [Version] | Should -Not -BeNullOrEmpty
64+
}
65+
66+
It 'Changelog and manifest versions are the same' {
67+
$changelogVersion -as [Version] | Should -Be ( $manifestData.Version -as [Version] )
68+
}
69+
}
70+
}
71+
72+
Describe 'Git tagging' -Skip {
73+
BeforeAll {
74+
$gitTagVersion = $null
75+
76+
# Ensure to only pull in a single git executable (in case multiple git's are found on path).
77+
if ($git = (Get-Command git -CommandType Application -ErrorAction SilentlyContinue)[0]) {
78+
$thisCommit = & $git log --decorate --oneline HEAD~1..HEAD
79+
if ($thisCommit -match $gitTagMatchRegEx) { $gitTagVersion = $matches[1] }
80+
}
81+
}
82+
83+
It 'Is tagged with a valid version' {
84+
$gitTagVersion | Should -Not -BeNullOrEmpty
85+
$gitTagVersion -as [Version] | Should -Not -BeNullOrEmpty
86+
}
87+
88+
It 'Matches manifest version' {
89+
$manifestData.Version -as [Version] | Should -Be ( $gitTagVersion -as [Version])
90+
}
91+
}

tests/Meta.tests.ps1

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
BeforeAll {
2+
3+
Set-StrictMode -Version latest
4+
5+
# Make sure MetaFixers.psm1 is loaded - it contains Get-TextFilesList
6+
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'MetaFixers.psm1') -Verbose:$false -Force
7+
8+
$projectRoot = $ENV:BHProjectPath
9+
if (-not $projectRoot) {
10+
$projectRoot = $PSScriptRoot
11+
}
12+
13+
$allTextFiles = Get-TextFilesList $projectRoot
14+
$unicodeFilesCount = 0
15+
$totalTabsCount = 0
16+
foreach ($textFile in $allTextFiles) {
17+
if (Test-FileUnicode $textFile) {
18+
$unicodeFilesCount++
19+
Write-Warning (
20+
"File $($textFile.FullName) contains 0x00 bytes." +
21+
" It probably uses Unicode/UTF-16 and needs to be converted to UTF-8." +
22+
" Use Fixer 'Get-UnicodeFilesList `$pwd | ConvertTo-UTF8'."
23+
)
24+
}
25+
$unicodeFilesCount | Should -Be 0
26+
27+
$fileName = $textFile.FullName
28+
(Get-Content $fileName -Raw) | Select-String "`t" | Foreach-Object {
29+
Write-Warning (
30+
"There are tabs in $fileName." +
31+
" Use Fixer 'Get-TextFilesList `$pwd | ConvertTo-SpaceIndentation'."
32+
)
33+
$totalTabsCount++
34+
}
35+
}
36+
}
37+
38+
Describe 'Text files formatting' {
39+
Context 'File encoding' {
40+
It "No text file uses Unicode/UTF-16 encoding" {
41+
$unicodeFilesCount | Should -Be 0
42+
}
43+
}
44+
45+
Context 'Indentations' {
46+
It "No text file use tabs for indentations" {
47+
$totalTabsCount | Should -Be 0
48+
}
49+
}
50+
}

tests/MetaFixers.psm1

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Taken with love from https://github.com/PowerShell/DscResource.Tests/blob/master/MetaFixers.psm1
2+
3+
<#
4+
This module helps fix problems, found by Meta.Tests.ps1
5+
#>
6+
7+
$ErrorActionPreference = 'stop'
8+
Set-StrictMode -Version latest
9+
10+
function ConvertTo-UTF8() {
11+
[CmdletBinding()]
12+
[OutputType([void])]
13+
param(
14+
[Parameter(Mandatory, ValueFromPipeline)]
15+
[System.IO.FileInfo]$FileInfo
16+
)
17+
18+
process {
19+
$content = Get-Content -Raw -Encoding Unicode -Path $FileInfo.FullName
20+
[System.IO.File]::WriteAllText($FileInfo.FullName, $content, [System.Text.Encoding]::UTF8)
21+
}
22+
}
23+
24+
function ConvertTo-SpaceIndentation() {
25+
[CmdletBinding()]
26+
[OutputType([void])]
27+
param(
28+
[Parameter(Mandatory, ValueFromPipeline)]
29+
[IO.FileInfo]$FileInfo
30+
)
31+
32+
process {
33+
$content = (Get-Content -Raw -Path $FileInfo.FullName) -replace "`t", ' '
34+
[IO.File]::WriteAllText($FileInfo.FullName, $content)
35+
}
36+
}
37+
38+
function Get-TextFilesList {
39+
[CmdletBinding()]
40+
[OutputType([IO.FileInfo])]
41+
param(
42+
[Parameter(Mandatory, ValueFromPipeline)]
43+
[string]$Root
44+
)
45+
46+
begin {
47+
$txtFileExtentions = @('.gitignore', '.gitattributes', '.ps1', '.psm1', '.psd1', '.json', '.xml', '.cmd', '.mof')
48+
}
49+
50+
process {
51+
Get-ChildItem -Path $Root -File -Recurse |
52+
Where-Object { $_.Extension -in $txtFileExtentions }
53+
}
54+
}
55+
56+
function Test-FileUnicode {
57+
[CmdletBinding()]
58+
[OutputType([bool])]
59+
param(
60+
[Parameter(Mandatory, ValueFromPipeline)]
61+
[IO.FileInfo]$FileInfo
62+
)
63+
64+
process {
65+
$bytes = [IO.File]::ReadAllBytes($FileInfo.FullName)
66+
$zeroBytes = @($bytes -eq 0)
67+
return [bool]$zeroBytes.Length
68+
}
69+
}
70+
71+
function Get-UnicodeFilesList() {
72+
[CmdletBinding()]
73+
[OutputType([IO.FileInfo])]
74+
param(
75+
[Parameter(Mandatory)]
76+
[string]$Root
77+
)
78+
79+
$root | Get-TextFilesList | Where-Object { Test-FileUnicode $_ }
80+
}

0 commit comments

Comments
 (0)