Skip to content

Commit

Permalink
Adds support for Register-ArgumentCompleter (#1979)
Browse files Browse the repository at this point in the history
  • Loading branch information
indented-automation committed Jun 23, 2021
1 parent 5bc79e2 commit 582c508
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 7 deletions.
75 changes: 74 additions & 1 deletion src/functions/assertions/HaveParameter.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,75 @@
}
}

function Get-ArgumentCompleter {
<#
.SYNOPSIS
Get custom argument completers registered in the current session.
.DESCRIPTION
Get custom argument completers registered in the current session.
By default Get-ArgumentCompleter lists all of the completers registered in the session.
.EXAMPLE
Get-ArgumentCompleter
Get all of the argument completers for PowerShell commands in the current session.
.EXAMPLE
Get-ArgumentCompleter -CommandName Invoke-ScriptAnalyzer
Get all of the argument completers used by the Invoke-ScriptAnalyzer command.
.EXAMPLE
Get-ArgumentCompleter -Native
Get all of the argument completers for native commands in the current session.
.NOTES
Author: Chris Dent
#>

[CmdletBinding()]
param (
# Filter results by command name.
[Parameter(Mandatory = $true)]
[String]$CommandName,

# Filter results by parameter name.
[Parameter(Mandatory = $true)]
[String]$ParameterName
)

$getExecutionContextFromTLS = [PowerShell].Assembly.GetType('System.Management.Automation.Runspaces.LocalPipeline').GetMethod(
'GetExecutionContextFromTLS',
[System.Reflection.BindingFlags]'Static, NonPublic'
)
$internalExecutionContext = $getExecutionContextFromTLS.Invoke(
$null,
[System.Reflection.BindingFlags]'Static, NonPublic',
$null,
$null,
$PSCulture
)

$argumentCompletersProperty = $internalExecutionContext.GetType().GetProperty(
'CustomArgumentCompleters',
[System.Reflection.BindingFlags]'NonPublic, Instance'
)
$argumentCompleters = $argumentCompletersProperty.GetGetMethod($true).Invoke(
$internalExecutionContext,
[System.Reflection.BindingFlags]'Instance, NonPublic, GetProperty',
$null,
@(),
$PSCulture
)

$completerName = '{0}:{1}' -f $CommandName, $ParameterName
if ($argumentCompleters.ContainsKey($completerName)) {
[PSCustomObject]@{
CommandName = $CommandName
ParameterName = $ParameterName
Definition = $argumentCompleters[$completerName]
}
}
}

if ($Type -is [string]) {
# parses type that is provided as a string in brackets (such as [int])
$parsedType = ($Type -replace '^\[(.*)\]$', '$1') -as [Type]
Expand Down Expand Up @@ -156,7 +225,7 @@
# PS5> [datetime]
[type]$actualType = $ActualValue.Parameters[$ParameterName].ParameterType
$testType = ($Type -eq $actualType)
$filters += "$(if ($Negate) {"not "})of type [$($Type.FullName)]"
$filters += "$(if ($Negate) { "not " })of type [$($Type.FullName)]"

if (-not $Negate -and -not $testType) {
$buts += "it was of type [$($actualType.FullName)]"
Expand All @@ -182,6 +251,10 @@

if ($HasArgumentCompleter) {
$testArgumentCompleter = $attributes | & $SafeCommands['Where-Object'] { $_ -is [ArgumentCompleter] }

if (-not $testArgumentCompleter) {
$testArgumentCompleter = Get-ArgumentCompleter -CommandName $ActualValue.Name -ParameterName $ParameterName
}
$filters += "has ArgumentCompletion"

if (-not $Negate -and -not $testArgumentCompleter) {
Expand Down
49 changes: 43 additions & 6 deletions tst/functions/assertions/HaveParameter.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,20 @@ InPesterModuleScope {
ForEach-Object { [System.Management.Automation.CompletionResult]::new( $_.Name, $_.Name, [System.Management.Automation.CompletionResultType]::ParameterValue, $_.Name ) }
}
)]
[String]$ParamWithArgumentCompleter = "./.git"
[String]$ParamWithArgumentCompleter = "./.git",

[Parameter()]
[String]$ParamWithRegisteredArgumentCompleter = "./.git"
)
}

Register-ArgumentCompleter -CommandName Invoke-DummyFunction -ParameterName ParamWithRegisteredArgumentCompleter -ScriptBlock {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)

& Get-ChildItem |
Where-Object { $_.Name -like "$wordToComplete*" } |
ForEach-Object { [System.Management.Automation.CompletionResult]::new( $_.Name, $_.Name, [System.Management.Automation.CompletionResultType]::ParameterValue, $_.Name ) }
}
}
else {
function Invoke-DummyFunction {
Expand Down Expand Up @@ -98,6 +109,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation" }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter" }
}
) {
param($ParameterName)
Expand All @@ -117,6 +129,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation"; ExpectedType = "String" }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = "String" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = "String" }
}
) {
param($ParameterName, $ExpectedType)
Expand All @@ -126,6 +139,7 @@ InPesterModuleScope {
if ($PSVersionTable.PSVersion.Major -ge 5) {
It "passes if the parameter <ParameterName> has an ArgumentCompleter" -TestCases @(
@{ParameterName = "ParamWithArgumentCompleter" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter" }
) {
param($ParameterName)
Get-Command "Invoke-DummyFunction" | Should -HaveParameter $ParameterName -HasArgumentCompleter
Expand All @@ -138,6 +152,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation"; ExpectedValue = "." }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedValue = "./.git" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedValue = "./.git" }
}
) {
param($ParameterName, $ExpectedValue)
Expand All @@ -149,6 +164,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation"; ExpectedType = [String]; ExpectedValue = "." }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = "String"; ExpectedValue = "./.git" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = "String"; ExpectedValue = "./.git" }
}
) {
param($ParameterName, $ExpectedType, $ExpectedValue)
Expand All @@ -162,6 +178,7 @@ InPesterModuleScope {
if ($PSVersionTable.PSVersion.Major -ge 5) {
It "passes if the parameter <ParameterName> exists, is of type <ExpectedType>, has a default value '<ExpectedValue>' and has an ArgumentCompleter" -TestCases @(
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = [String]; ExpectedValue = "./.git" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = [String]; ExpectedValue = "./.git" }
) {
param($ParameterName, $ExpectedType, $ExpectedValue)
Get-Command "Invoke-DummyFunction" | Should -HaveParameter $ParameterName -Type $ExpectedType -DefaultValue $ExpectedValue -HasArgumentCompleter
Expand All @@ -186,6 +203,8 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation" }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter" }

}
@{ParameterName = "InputObject" }
) {
Expand All @@ -199,6 +218,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation"; ExpectedType = [DateTime] }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = "DateTime" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = "DateTime" }
}
@{ParameterName = "InputObject"; ExpectedType = [String] }
) {
Expand All @@ -224,6 +244,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation"; ExpectedValue = "" }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedValue = "." }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedValue = "." }
}
@{ParameterName = "InputObject"; ExpectedValue = "" }
) {
Expand All @@ -237,6 +258,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation"; ExpectedType = [DateTime]; ExpectedValue = "." }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = "String"; ExpectedValue = "" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = [String]; ExpectedValue = "" }
}
@{ParameterName = "InputObject"; ExpectedType = [String]; ExpectedValue = "" }
) {
Expand All @@ -254,6 +276,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithNotNullOrEmptyValidation"; ExpectedType = [DateTime]; ExpectedValue = "." }
@{ParameterName = "ParamWithScriptValidation"; ExpectedType = [String]; ExpectedValue = "." }
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = [String]; ExpectedValue = "." }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = [String]; ExpectedValue = "." }
@{ParameterName = "InputObject"; ExpectedType = [String]; ExpectedValue = "." }
) {
param($ParameterName, $ExpectedType, $ExpectedValue)
Expand Down Expand Up @@ -284,7 +307,7 @@ InPesterModuleScope {
}
}

Describe "Should -Not -HavePameter" {
Describe "Should -Not -HaveParameter" {
BeforeAll {
. $functionsBlock
}
Expand All @@ -302,6 +325,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation" }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter" }
}
@{ParameterName = "InputObject" }
) {
Expand All @@ -314,6 +338,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation"; ExpectedType = "[TimeSpan]" }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = [TimeSpan] }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = [Timespan] }
}
@{ParameterName = "InputObject"; ExpectedType = "[Object]" }
) {
Expand All @@ -327,6 +352,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation"; ExpectedType = "[Int32]"; ExpectedValue = ".." }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = [TimeSpan]; ExpectedValue = "." }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = [TimeSpan]; ExpectedValue = "." }
}
@{ParameterName = "InputObject"; ExpectedType = "[Object]"; ExpectedValue = "" }
) {
Expand All @@ -352,6 +378,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation" }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter" }
}
) {
param($ParameterName)
Expand All @@ -371,6 +398,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation"; ExpectedType = [String] }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = "String" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = "String" }
}
) {
param($ParameterName, $ExpectedType)
Expand All @@ -383,6 +411,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation"; ExpectedType = "[String]"; ExpectedValue = ".." }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = [String]; ExpectedValue = "." }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = [String]; ExpectedValue = "." }
}
) {
param($ParameterName, $ExpectedType, $ExpectedValue)
Expand All @@ -392,6 +421,7 @@ InPesterModuleScope {
if ($PSVersionTable.PSVersion.Major -ge 5) {
It "fails if the parameter <ParameterName> has an ArgumentCompleter" -TestCases @(
@{ParameterName = "ParamWithArgumentCompleter" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter" }
) {
param($ParameterName)
{ Get-Command "Invoke-DummyFunction" | Should -Not -HaveParameter $ParameterName -HasArgumentCompleter } | Verify-AssertionFailed
Expand All @@ -404,6 +434,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation"; ExpectedValue = "." }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedValue = "./.git" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedValue = "./.git" }
}
) {
param($ParameterName, $ExpectedValue)
Expand All @@ -416,6 +447,7 @@ InPesterModuleScope {
@{ParameterName = "ParamWithScriptValidation"; ExpectedType = [String]; ExpectedValue = "." }
if ($PSVersionTable.PSVersion.Major -ge 5) {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = "String"; ExpectedValue = "./.git" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = "String"; ExpectedValue = "." }
}
) {
param($ParameterName, $ExpectedType, $ExpectedValue)
Expand All @@ -427,7 +459,9 @@ InPesterModuleScope {
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = [String]; ExpectedValue = "./.git" }
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = [DateTime]; ExpectedValue = "./.git" }
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = [DateTime]; ExpectedValue = "" }

@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = [String]; ExpectedValue = "./.git" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = [DateTime]; ExpectedValue = "./.git" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = [DateTime]; ExpectedValue = "" }
) {
param($ParameterName, $ExpectedType, $ExpectedValue)
{ Get-Command "Invoke-DummyFunction" | Should -Not -HaveParameter $ParameterName -Type $ExpectedType -DefaultValue $ExpectedValue -HasArgumentCompleter } | Verify-AssertionFailed
Expand All @@ -445,9 +479,12 @@ InPesterModuleScope {
}

if ($PSVersionTable.PSVersion.Major -ge 5) {
It "returns the correct assertion message when parameter ParamWithNotNullOrEmptyValidation is not mandatory, of the wrong type, has a different default value than expected and has no ArgumentCompleter" {
$err = { Get-Command "Invoke-DummyFunction" | Should -Not -HaveParameter ParamWithArgumentCompleter -Type [String] -DefaultValue "./.git" -HasArgumentCompleter -Because 'of reasons' } | Verify-AssertionFailed
$err.Exception.Message | Verify-Equal "Expected command Invoke-DummyFunction to not have a parameter ParamWithArgumentCompleter, not of type [System.String], the default value not to be './.git' and has ArgumentCompletion, because of reasons, but it was of type [System.String], the default value was './.git' and has ArgumentCompletion."
It "returns the correct assertion message when parameter ParamWithNotNullOrEmptyValidation is not mandatory, of the wrong type, has a different default value than expected and has no ArgumentCompleter" -TestCases @(
@{ParameterName = "ParamWithArgumentCompleter"; ExpectedType = "System.String"; ExpectedValue = "./.git" }
@{ParameterName = "ParamWithRegisteredArgumentCompleter"; ExpectedType = "System.String"; ExpectedValue = "./.git" }
) {
$err = { Get-Command "Invoke-DummyFunction" | Should -Not -HaveParameter $ParameterName -Type $ExpectedType -DefaultValue $ExpectedValue -HasArgumentCompleter -Because 'of reasons' } | Verify-AssertionFailed
$err.Exception.Message | Verify-Equal "Expected command Invoke-DummyFunction to not have a parameter $ParameterName, not of type [$ExpectedType], the default value not to be '$ExpectedValue' and has ArgumentCompletion, because of reasons, but it was of type [$ExpectedType], the default value was '$ExpectedValue' and has ArgumentCompletion."
}
}
}
Expand Down

0 comments on commit 582c508

Please sign in to comment.