Skip to content

Commit

Permalink
HaveParameter is not dependent on format of param block (#2288)
Browse files Browse the repository at this point in the history
  • Loading branch information
brianwest authored Mar 31, 2023
1 parent 8a6a144 commit a8ad480
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 54 deletions.
88 changes: 34 additions & 54 deletions src/functions/assertions/HaveParameter.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -31,77 +31,52 @@
}

function Get-ParameterInfo {
param(
[Parameter( Mandatory = $true )]
param (
[Parameter(Mandatory = $true)]
[Management.Automation.CommandInfo]$Command
)
<#
.SYNOPSIS
Use Tokenize to get information about the parameter block of a command
Use AST to get information about the parameter block of a command
.DESCRIPTION
In order to get information about the parameter block of a command,
several tools can be used (Get-Command, AST, etc).
In order to get the default value of a parameter, AST is the easiest
way to go; but AST was only introduced with PSv3.
This function creates an object with information about parameters
using the Tokenize
way to go
.NOTES
Author: Chris Dent
Author: Brian West
#>

function Get-TokenGroup {
param(
[Parameter( Mandatory = $true )]
[System.Management.Automation.PSToken[]]$tokens
)
$i = $j = 0
do {
$token = $tokens[$i]
if ($token.Type -eq 'GroupStart') {
$j++
}
if ($token.Type -eq 'GroupEnd') {
$j--
}
if (-not $token.PSObject.Properties.Item('Depth')) {
$token.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Depth", $j))
}
$token

$i++
} until ($j -eq 0 -or $i -ge $tokens.Count)
}

$errors = $null
$tokens = [System.Management.Automation.PSParser]::Tokenize($Command.Definition, [Ref]$errors)
# Find parameters
$ast = $Command.ScriptBlock.Ast

# Find param block
$start = $tokens.IndexOf(($tokens | & $SafeCommands['Where-Object'] { $_.Content -eq 'param' } | & $SafeCommands['Select-Object'] -First 1)) + 1
$paramBlock = Get-TokenGroup $tokens[$start..($tokens.Count - 1)]

for ($i = 0; $i -lt $paramBlock.Count; $i++) {
$token = $paramBlock[$i]
if ($null -ne $ast) {
if ($null -ne $ast.Parameters) {
$parameters = $ast.Parameters
}
elseif ($null -ne $ast.Body.ParamBlock) {
$parameters = $ast.Body.ParamBlock.Parameters
}
else {
return
}

if ($token.Depth -eq 1 -and $token.Type -eq 'Variable') {
foreach ($parameter in $parameters) {
$paramInfo = & $SafeCommands['New-Object'] PSObject -Property @{
Name = $token.Content
Name = $parameter.Name.VariablePath.UserPath
DefaultValueType = $parameter.StaticType.Name
Type = "[$($parameter.StaticType.Name.ToLower())]"
} | & $SafeCommands['Select-Object'] Name, Type, DefaultValue, DefaultValueType

if ($paramBlock[$i + 1].Content -ne ',') {
$value = $paramBlock[$i + 2]
if ($value.Type -eq 'GroupStart') {
$tokenGroup = Get-TokenGroup $paramBlock[($i + 2)..($paramBlock.Count - 1)]
$paramInfo.DefaultValue = [String]::Join('', ($tokenGroup | & $SafeCommands['ForEach-Object'] { $_.Content }))
$paramInfo.DefaultValueType = 'Expression'
if ($null -ne $parameter.DefaultValue) {
if ($parameter.DefaultValue.PSObject.Properties['Value']) {
$paramInfo.DefaultValue = $parameter.DefaultValue.Value
}
else {
$paramInfo.DefaultValue = $value.Content
$paramInfo.DefaultValueType = $value.Type
$paramInfo.DefaultValue = $parameter.DefaultValue.Extent.Text
}
}
if ($paramBlock[$i - 1].Type -eq 'Type') {
$paramInfo.Type = $paramBlock[$i - 1].Content
}

$paramInfo
}
}
Expand Down Expand Up @@ -249,7 +224,12 @@

if ($PSBoundParameters.Keys -contains "DefaultValue") {
$parameterMetadata = Get-ParameterInfo $ActualValue | & $SafeCommands['Where-Object'] { $_.Name -eq $ParameterName }
$actualDefault = if ($parameterMetadata.DefaultValue) { $parameterMetadata.DefaultValue } else { "" }
$actualDefault = if ($parameterMetadata.DefaultValue) {
$parameterMetadata.DefaultValue
}
else {
""
}
$testDefault = ($actualDefault -eq $DefaultValue)
$filters += "the default value$(if ($Negate) {" not"}) to be $(Format-Nicely $DefaultValue)"

Expand Down Expand Up @@ -290,10 +270,10 @@
$faultyAliases += $AliasValue
}
}
if($faultyAliases.Count -ge 1) {
if ($faultyAliases.Count -ge 1) {
$aliases = $(Join-And ($faultyAliases -replace '^|$', "'"))
$singular = $faultyAliases.Count -eq 1
if($Negate) {
if ($Negate) {
$buts += "it has $(if($singular) {"an alias"} else {"the aliases"} ) $aliases"
}
else {
Expand Down
11 changes: 11 additions & 0 deletions tst/functions/assertions/HaveParameter.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ InPesterModuleScope {
Get-Command "Invoke-DummyFunction" | Should -HaveParameter $ParameterName -DefaultValue $ExpectedValue
}

It "passes if the paramblock has opening parenthesis on new line and parameter has a default value" {
function Test-Paramblock {
param
(
$Name = 'test'
)
}

Get-Command -Name 'Test-Paramblock' | Should -HaveParameter -ParameterName 'Name' -DefaultValue 'test'
}

It "passes if the parameter <ParameterName> exists, is of type <ExpectedType> and has a default value '<ExpectedValue>'" -TestCases @(
@{ParameterName = "ParamWithNotNullOrEmptyValidation"; ExpectedType = [DateTime]; ExpectedValue = "(Get-Date)" }
@{ParameterName = "ParamWithScriptValidation"; ExpectedType = [String]; ExpectedValue = "." }
Expand Down

0 comments on commit a8ad480

Please sign in to comment.