PesterConverter.psm1
#Region '.\prefix.ps1' -1 $script:dscResourceCommonModulePath = Join-Path -Path $PSScriptRoot -ChildPath 'Modules/DscResource.Common' Import-Module -Name $script:dscResourceCommonModulePath $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' #EndRegion '.\prefix.ps1' 5 #Region '.\Private\Convert-ShouldBe.ps1' -1 <# .SYNOPSIS Converts a command `Should -Be` to the specified Pester syntax. .DESCRIPTION The Convert-ShouldBe function is used to convert a command `Should -Be` to the specified Pester syntax. .PARAMETER CommandAst The CommandAst object representing the command to be converted. .PARAMETER Pester6 Specifies that the command should be converted to Pester version 6 syntax. .PARAMETER UseNamedParameters Specifies whether to use named parameters in the converted syntax. .PARAMETER UsePositionalParameters Specifies whether to use positional parameters in the converted syntax, where supported. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -Be "Test"') Convert-ShouldBe -CommandAst $commandAst -Pester6 This example converts the `Should -Be "Test"` command to Pester 6 syntax. .NOTES Pester 5 Syntax: Should -Be [[-ActualValue] <Object>] [-Not] [[-ExpectedValue] <Object>] [-Because <Object>] Positional parameters: Position 1: ExpectedValue Position 2: Because Position 3: ActualValue Pester 6 Syntax: Should-Be [[-Actual] <Object>] [-Expected] <Object> [-Because <String>] Should-NotBe [[-Actual] <Object>] [-Expected] <Object> [-Because <String>] Positional parameters: Position 1: Expected Position 2: Actual #> function Convert-ShouldBe { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter(Mandatory = $true, ParameterSetName = 'Pester6')] [System.Management.Automation.SwitchParameter] $Pester6, [Parameter()] [System.Management.Automation.SwitchParameter] $UseNamedParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $UsePositionalParameters ) $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters MutuallyExclusiveList1 = @('UseNamedParameters') MutuallyExclusiveList2 = @('UsePositionalParameters') } Assert-BoundParameter @assertBoundParameterParameters Write-Debug -Message ('Parsing the command AST: {0}' -f $CommandAst.Extent.Text) # Determine if the command is negated $isNegated = Test-PesterCommandNegated -CommandAst $CommandAst $sourceSyntaxVersion = Get-PesterCommandSyntaxVersion -CommandAst $CommandAst # Parse the command elements and convert them to Pester 6 syntax if ($PSCmdlet.ParameterSetName -eq 'Pester6') { Write-Debug -Message ('Converting from Pester v{0} to Pester v6 syntax.' -f $sourceSyntaxVersion) # Add the correct Pester command based on negation if ($isNegated) { $newExtentText = 'Should-NotBe' } else { $newExtentText = 'Should-Be' } $getPesterCommandParameterParameters = @{ CommandAst = $CommandAst CommandName = 'Should' IgnoreParameter = @( 'Be' 'EQ' 'Not' ) PositionalParameter = @( 'ExpectedValue' 'Because' 'ActualValue' ) } $commandParameters = Get-PesterCommandParameter @getPesterCommandParameterParameters # Parameter 'Because' is only supported as named parameter in Pester 6 syntax. if ($commandParameters.Because) { $commandParameters.Because.Positional = $false } # Determine if named or positional parameters should be forcibly used if ($UseNamedParameters.IsPresent) { $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) } elseif ($UsePositionalParameters.IsPresent) { # First set all to named parameters $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) <# If a previous positional parameter is missing then the ones behind it cannot be set to positional. #> if ($commandParameters.ExpectedValue) { $commandParameters.ExpectedValue.Positional = $true if ($commandParameters.ActualValue) { $commandParameters.ActualValue.Positional = $true } } } $newExtentText += $commandParameters.ExpectedValue.Positional ? (' {0}' -f $commandParameters.ExpectedValue.ExtentText) : '' $newExtentText += $commandParameters.ActualValue.Positional ? (' {0}' -f $commandParameters.ActualValue.ExtentText) : '' # Holds the new parameter names so they can be added in alphabetical order. $parameterNames = @() foreach ($currentParameter in $commandParameters.Keys) { if ($commandParameters.$currentParameter.Positional -eq $true) { continue } switch ($currentParameter) { 'ActualValue' { $parameterNames += @{ Actual = 'ActualValue' } break } 'ExpectedValue' { $parameterNames += @{ Expected = 'ExpectedValue' } break } default { $parameterNames += @{ $currentParameter = $currentParameter } break } } } # This handles the named parameters in the command elements, added in alphabetical order. foreach ($currentParameter in $parameterNames.Keys | Sort-Object) { $originalParameterName = $parameterNames.$currentParameter $newExtentText += ' -{0} {1}' -f $currentParameter, $commandParameters.$originalParameterName.ExtentText } } Write-Debug -Message ('Converted the command `{0}` to `{1}`.' -f $CommandAst.Extent.Text, $newExtentText) return $newExtentText } #EndRegion '.\Private\Convert-ShouldBe.ps1' 203 #Region '.\Private\Convert-ShouldBeExactly.ps1' -1 <# .SYNOPSIS Converts a command `Should -BeExactly` to the specified Pester syntax. .DESCRIPTION The Convert-ShouldBe function is used to convert a command `Should -BeExactly` to the specified Pester syntax. .PARAMETER CommandAst The CommandAst object representing the command to be converted. .PARAMETER Pester6 Specifies that the command should be converted to Pester version 6 syntax. .PARAMETER UseNamedParameters Specifies whether to use named parameters in the converted syntax. .PARAMETER UsePositionalParameters Specifies whether to use positional parameters in the converted syntax, where supported. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -BeExactly "Test"') Convert-ShouldBeExactly -CommandAst $commandAst -Pester6 This example converts the `Should -BeExactly "Test"` command to Pester 6 syntax. .NOTES Pester 5 Syntax: Should -BeExactly [[-ActualValue] <Object>] [-Not] [[-ExpectedValue] <Object>] [-Because <Object>] Positional parameters: Position 1: ExpectedValue Position 2: Because Position 3: ActualValue Pester 6 Syntax: Should-BeString [[-Actual] <Object>] [[-Expected] <String>] [-Because <String>] [-CaseSensitive] [-IgnoreWhitespace] Should-NotBeString [[-Actual] <Object>] [[-Expected] <String>] [-Because <String>] [-CaseSensitive] [-IgnoreWhitespace] Positional parameters: Position 1: Expected Position 2: Actual #> function Convert-ShouldBeExactly { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter(Mandatory = $true, ParameterSetName = 'Pester6')] [System.Management.Automation.SwitchParameter] $Pester6, [Parameter()] [System.Management.Automation.SwitchParameter] $UseNamedParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $UsePositionalParameters ) $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters MutuallyExclusiveList1 = @('UseNamedParameters') MutuallyExclusiveList2 = @('UsePositionalParameters') } Assert-BoundParameter @assertBoundParameterParameters Write-Debug -Message ('Parsing the command AST: {0}' -f $CommandAst.Extent.Text) # Determine if the command is negated $isNegated = Test-PesterCommandNegated -CommandAst $CommandAst $sourceSyntaxVersion = Get-PesterCommandSyntaxVersion -CommandAst $CommandAst # Parse the command elements and convert them to Pester 6 syntax if ($PSCmdlet.ParameterSetName -eq 'Pester6') { Write-Debug -Message ('Converting from Pester v{0} to Pester v6 syntax.' -f $sourceSyntaxVersion) # Add the correct Pester command based on negation if ($isNegated) { $newExtentText = 'Should-NotBeString' } else { $newExtentText = 'Should-BeString' } # Always add the `-CaseSensitive` parameter since BeExactly was case-sensitive. $newExtentText += ' -CaseSensitive' $getPesterCommandParameterParameters = @{ CommandAst = $CommandAst CommandName = 'Should' IgnoreParameter = @( 'CEQ' 'BeExactly' 'Not' ) PositionalParameter = @( 'ExpectedValue' 'Because' 'ActualValue' ) } $commandParameters = Get-PesterCommandParameter @getPesterCommandParameterParameters # Parameter 'Because' is only supported as named parameter in Pester 6 syntax. if ($commandParameters.Because) { $commandParameters.Because.Positional = $false } # Determine if named or positional parameters should be forcibly used if ($UseNamedParameters.IsPresent) { $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) } elseif ($UsePositionalParameters.IsPresent) { # First set all to named parameters $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) <# If a previous positional parameter is missing then the ones behind it cannot be set to positional. #> if ($commandParameters.ExpectedValue) { $commandParameters.ExpectedValue.Positional = $true if ($commandParameters.ActualValue) { $commandParameters.ActualValue.Positional = $true } } } $newExtentText += $commandParameters.ExpectedValue.Positional ? (' {0}' -f $commandParameters.ExpectedValue.ExtentText) : '' $newExtentText += $commandParameters.ActualValue.Positional ? (' {0}' -f $commandParameters.ActualValue.ExtentText) : '' # Holds the new parameter names so they can be added in alphabetical order. $parameterNames = @() foreach ($currentParameter in $commandParameters.Keys) { if ($commandParameters.$currentParameter.Positional -eq $true) { continue } switch ($currentParameter) { 'ActualValue' { $parameterNames += @{ Actual = 'ActualValue' } break } 'ExpectedValue' { $parameterNames += @{ Expected = 'ExpectedValue' } break } default { $parameterNames += @{ $currentParameter = $currentParameter } break } } } # This handles the named parameters in the command elements, added in alphabetical order. foreach ($currentParameter in $parameterNames.Keys | Sort-Object) { $originalParameterName = $parameterNames.$currentParameter $newExtentText += ' -{0} {1}' -f $currentParameter, $commandParameters.$originalParameterName.ExtentText } } Write-Debug -Message ('Converted the command `{0}` to `{1}`.' -f $CommandAst.Extent.Text, $newExtentText) return $newExtentText } #EndRegion '.\Private\Convert-ShouldBeExactly.ps1' 206 #Region '.\Private\Convert-ShouldBeFalse.ps1' -1 <# .SYNOPSIS Converts a command `Should -BeFalse` to the specified Pester syntax. .DESCRIPTION The Convert-ShouldBeFalse function is used to convert a command `Should -BeFalse` to the specified Pester syntax. .PARAMETER CommandAst The CommandAst object representing the command to be converted. .PARAMETER Pester6 Specifies that the command should be converted to Pester version 6 syntax. .PARAMETER UseNamedParameters Specifies whether to use named parameters in the converted syntax. .PARAMETER UsePositionalParameters Specifies whether to use positional parameters in the converted syntax, where supported. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -BeFalse') Convert-ShouldBeFalse -CommandAst $commandAst -Pester6 This example converts the `Should -BeFalse` command to Pester 6 syntax. .NOTES Pester 5 Syntax: Should -BeFalse [[-ActualValue] <Object>] [[-Because] <Object>] [-Not] Positional parameters: Position 1: Because Position 2: ActualValue Pester 6 Syntax: Should-BeFalse [[-Actual] <Object>] [[-Because] <String>] Positional parameters: Position 1: Actual Position 2: Because Conversion notes: If the Pester 5 syntax is negated it must be converted to Should-BeTrue. #> function Convert-ShouldBeFalse { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter(Mandatory = $true, ParameterSetName = 'Pester6')] [System.Management.Automation.SwitchParameter] $Pester6, [Parameter()] [System.Management.Automation.SwitchParameter] $UseNamedParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $UsePositionalParameters ) $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters MutuallyExclusiveList1 = @('UseNamedParameters') MutuallyExclusiveList2 = @('UsePositionalParameters') } Assert-BoundParameter @assertBoundParameterParameters Write-Debug -Message ('Parsing the command AST: {0}' -f $CommandAst.Extent.Text) # Determine if the command is negated $isNegated = Test-PesterCommandNegated -CommandAst $CommandAst $sourceSyntaxVersion = Get-PesterCommandSyntaxVersion -CommandAst $CommandAst # Parse the command elements and convert them to Pester 6 syntax if ($PSCmdlet.ParameterSetName -eq 'Pester6') { Write-Debug -Message ('Converting from Pester v{0} to Pester v6 syntax.' -f $sourceSyntaxVersion) # Add the correct Pester command based on negation if ($isNegated) { $newExtentText = 'Should-BeTrue' } else { $newExtentText = 'Should-BeFalse' } $getPesterCommandParameterParameters = @{ CommandAst = $CommandAst CommandName = 'Should' IgnoreParameter = 'BeFalse', 'Not' PositionalParameter = 'Because', 'ActualValue' } $commandParameters = Get-PesterCommandParameter @getPesterCommandParameterParameters # Determine if named or positional parameters should be forcibly used if ($UseNamedParameters.IsPresent) { $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) } elseif ($UsePositionalParameters.IsPresent) { # First set all to named parameters $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) <# If a previous positional parameter is missing then the ones behind it cannot be set to positional. #> if ($commandParameters.ActualValue) { $commandParameters.ActualValue.Positional = $true if ($commandParameters.Because) { $commandParameters.Because.Positional = $true } } } $newExtentText += $commandParameters.ActualValue.Positional ? (' {0}' -f $commandParameters.ActualValue.ExtentText) : '' $newExtentText += $commandParameters.Because.Positional ? (' {0}' -f $commandParameters.Because.ExtentText) : '' # Holds the new parameter names so they can be added in alphabetical order. $parameterNames = @() foreach ($currentParameter in $commandParameters.Keys) { if ($commandParameters.$currentParameter.Positional -eq $true) { continue } switch ($currentParameter) { 'ActualValue' { $parameterNames += @{ Actual = 'ActualValue' } break } default { $parameterNames += @{ $currentParameter = $currentParameter } break } } } # This handles the named parameters in the command elements, added in alphabetical order. foreach ($currentParameter in $parameterNames.Keys | Sort-Object) { $originalParameterName = $parameterNames.$currentParameter $newExtentText += ' -{0} {1}' -f $currentParameter, $commandParameters.$originalParameterName.ExtentText } } Write-Debug -Message ('Converted the command `{0}` to `{1}`.' -f $CommandAst.Extent.Text, $newExtentText) return $newExtentText } #EndRegion '.\Private\Convert-ShouldBeFalse.ps1' 181 #Region '.\Private\Convert-ShouldBeNullOrEmpty.ps1' -1 <# .SYNOPSIS Converts a command `Should -BeNullOrEmpty` to the specified Pester syntax. .DESCRIPTION The Convert-ShouldBeNullOrEmpty function is used to convert a command `Should -BeNullOrEmpty` to the specified Pester syntax. .PARAMETER CommandAst The CommandAst object representing the command to be converted. .PARAMETER Pester6 Specifies that the command should be converted to Pester version 6 syntax. .PARAMETER UseNamedParameters Specifies whether to use named parameters in the converted syntax. .PARAMETER UsePositionalParameters Specifies whether to use positional parameters in the converted syntax, where supported. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -BeNullOrEmpty') Convert-ShouldBeNullOrEmpty -CommandAst $commandAst -Pester6 This example converts the `Should -BeNullOrEmpty` command to Pester 6 syntax. .NOTES Pester 5 Syntax: Should -BeNullOrEmpty [[-ActualValue] <Object>] [[-Because] <string>] [-Not] Positional parameters: Position 1: Because Position 2: ActualValue Pester 6 Syntax: Should-BeFalsy [[-Actual] <Object>] [[-Because] <String>] Should-BeTruthy [[-Actual] <Object>] [[-Because] <String>] Positional parameters: Position 1: Actual Position 2: Because Conversion notes: If the Pester 5 syntax is negated it must be converted to Should-BeTruthy. If the Pester 5 syntax uses positional parameters, the conversion must convert position 1 to position 2 and vice versa. It should output informational message that the user should review the converted commands to evaluate if it should stay Should-BeFalsy or if Should-BeNull or Should-BeEmptyString should be used instead. #> function Convert-ShouldBeNullOrEmpty { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter(Mandatory = $true, ParameterSetName = 'Pester6')] [System.Management.Automation.SwitchParameter] $Pester6, [Parameter()] [System.Management.Automation.SwitchParameter] $UseNamedParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $UsePositionalParameters ) $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters MutuallyExclusiveList1 = @('UseNamedParameters') MutuallyExclusiveList2 = @('UsePositionalParameters') } Assert-BoundParameter @assertBoundParameterParameters Write-Debug -Message ('Parsing the command AST: {0}' -f $CommandAst.Extent.Text) # Determine if the command is negated $isNegated = Test-PesterCommandNegated -CommandAst $CommandAst $sourceSyntaxVersion = Get-PesterCommandSyntaxVersion -CommandAst $CommandAst # Parse the command elements and convert them to Pester 6 syntax if ($PSCmdlet.ParameterSetName -eq 'Pester6') { Write-Debug -Message ('Converting from Pester v{0} to Pester v6 syntax.' -f $sourceSyntaxVersion) # Add the correct Pester command based on negation if ($isNegated) { $newExtentText = 'Should-BeTruthy' } else { $newExtentText = 'Should-BeFalsy' } $getPesterCommandParameterParameters = @{ CommandAst = $CommandAst CommandName = 'Should' IgnoreParameter = 'BeNullOrEmpty', 'Not' PositionalParameter = 'Because', 'ActualValue' } $commandParameters = Get-PesterCommandParameter @getPesterCommandParameterParameters # Determine if named or positional parameters should be forcibly used if ($UseNamedParameters.IsPresent) { $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) } elseif ($UsePositionalParameters.IsPresent) { # First set all to named parameters $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) <# If a previous positional parameter is missing then the ones behind it cannot be set to positional. #> if ($commandParameters.ActualValue) { $commandParameters.ActualValue.Positional = $true if ($commandParameters.Because) { $commandParameters.Because.Positional = $true } } } $newExtentText += $commandParameters.ActualValue.Positional ? (' {0}' -f $commandParameters.ActualValue.ExtentText) : '' $newExtentText += $commandParameters.Because.Positional ? (' {0}' -f $commandParameters.Because.ExtentText) : '' # Holds the new parameter names so they can be added in alphabetical order. $parameterNames = @() foreach ($currentParameter in $commandParameters.Keys) { if ($commandParameters.$currentParameter.Positional -eq $true) { continue } switch ($currentParameter) { 'ActualValue' { $parameterNames += @{ Actual = 'ActualValue' } break } default { $parameterNames += @{ $currentParameter = $currentParameter } break } } } # This handles the named parameters in the command elements, added in alphabetical order. foreach ($currentParameter in $parameterNames.Keys | Sort-Object) { $originalParameterName = $parameterNames.$currentParameter $newExtentText += ' -{0} {1}' -f $currentParameter, $commandParameters.$originalParameterName.ExtentText } } Write-Debug -Message ('Converted the command `{0}` to `{1}`.' -f $CommandAst.Extent.Text, $newExtentText) return $newExtentText } #EndRegion '.\Private\Convert-ShouldBeNullOrEmpty.ps1' 189 #Region '.\Private\Convert-ShouldBeOfType.ps1' -1 <# .SYNOPSIS Converts a command `Should -BeOfType` to the specified Pester syntax. .DESCRIPTION The Convert-ShouldBe function is used to convert a command `Should -BeOfType` to the specified Pester syntax. .PARAMETER CommandAst The CommandAst object representing the command to be converted. .PARAMETER Pester6 Specifies that the command should be converted to Pester version 6 syntax. .PARAMETER UseNamedParameters Specifies whether to use named parameters in the converted syntax. .PARAMETER UsePositionalParameters Specifies whether to use positional parameters in the converted syntax, where supported. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -BeOfType [System.String]') Convert-ShouldBeOfType -CommandAst $commandAst -Pester6 This example converts the `Should -BeOfType [System.String]` command to Pester 6 syntax. .NOTES Pester 5 Syntax: Should -BeOfType [[-ActualValue] <Object>] [[-ExpectedType] <Object>] [[-Because] <string>] [-Not] Positional parameters: Position 1: ExpectedValue Position 2: Because Pester 6 Syntax: Should-HaveType [[-Actual] <Object>] [-Expected] <Type> [-Because <String>] Should-NotHaveType [[-Actual] <Object>] [-Expected] <Object> [-Because <String>] Positional parameters: Position 1: Expected Position 2: Actual Conversion notes: The expected value should be surrounded by parenthesis after conversion in v6, e.g. `[System.String]` -> `([System.String])`. The `-ActualValue` parameter is only supported as a named parameter in v5 even if syntax says otherwise. If ActualValue is used it will concatenate with the Because value and wrongly always return an array. Did not find a way to prevent that. The `-Not` parameter is not supported in v6. It should be changed to the command `Should-NotHaveType`. The `-Because` parameter is only supported as a named parameter in v6. #> function Convert-ShouldBeOfType { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter(Mandatory = $true, ParameterSetName = 'Pester6')] [System.Management.Automation.SwitchParameter] $Pester6, [Parameter()] [System.Management.Automation.SwitchParameter] $UseNamedParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $UsePositionalParameters ) $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters MutuallyExclusiveList1 = @('UseNamedParameters') MutuallyExclusiveList2 = @('UsePositionalParameters') } Assert-BoundParameter @assertBoundParameterParameters Write-Debug -Message ('Parsing the command AST: {0}' -f $CommandAst.Extent.Text) # Determine if the command is negated $isNegated = Test-PesterCommandNegated -CommandAst $CommandAst $sourceSyntaxVersion = Get-PesterCommandSyntaxVersion -CommandAst $CommandAst # Parse the command elements and convert them to Pester 6 syntax if ($PSCmdlet.ParameterSetName -eq 'Pester6') { Write-Debug -Message ('Converting from Pester v{0} to Pester v6 syntax.' -f $sourceSyntaxVersion) # Add the correct Pester command based on negation if ($isNegated) { $newExtentText = 'Should-NotHaveType' } else { $newExtentText = 'Should-HaveType' } $getPesterCommandParameterParameters = @{ CommandAst = $CommandAst CommandName = 'Should' IgnoreParameter = @( 'BeOfType' 'HaveType' 'Not' ) PositionalParameter = @( 'ExpectedValue' 'Because' ) NamedParameter = @( 'ActualValue' ) } $commandParameters = Get-PesterCommandParameter @getPesterCommandParameterParameters # Parameter 'Because' is only supported as named parameter in Pester 6 syntax. if ($commandParameters.Because) { $commandParameters.Because.Positional = $false } # Determine if named or positional parameters should be forcibly used if ($UseNamedParameters.IsPresent) { $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) } elseif ($UsePositionalParameters.IsPresent) { # First set all to named parameters $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) <# If a previous positional parameter is missing then the ones behind it cannot be set to positional. #> if ($commandParameters.ExpectedValue) { $commandParameters.ExpectedValue.Positional = $true if ($commandParameters.ActualValue) { $commandParameters.ActualValue.Positional = $true } } } # '[System.String]' -match '^\[.+\]$' if ($commandParameters.ExpectedValue.Positional) { # Add the expected value in parenthesis only if the extent text is a type defined in square brackets. $extentTextFormat = $commandParameters.ExpectedValue.ExtentText -match '^\[.+\]$' ? ' ({0})' : ' {0}' $newExtentText += $extentTextFormat -f $commandParameters.ExpectedValue.ExtentText } $newExtentText += $commandParameters.ActualValue.Positional ? (' {0}' -f $commandParameters.ActualValue.ExtentText) : '' # Holds the new parameter names so they can be added in alphabetical order. $parameterNames = @() foreach ($currentParameter in $commandParameters.Keys) { if ($commandParameters.$currentParameter.Positional -eq $true) { continue } switch ($currentParameter) { 'ActualValue' { $parameterNames += @{ Actual = 'ActualValue' } break } 'ExpectedValue' { $parameterNames += @{ Expected = 'ExpectedValue' } break } default { $parameterNames += @{ $currentParameter = $currentParameter } break } } } # This handles the named parameters in the command elements, added in alphabetical order. foreach ($currentParameter in $parameterNames.Keys | Sort-Object) { $originalParameterName = $parameterNames.$currentParameter # Add the expected value in parenthesis only if the extent text is a type defined in square brackets. $extentTextFormat = $originalParameterName -eq 'ExpectedValue' -and $commandParameters.$originalParameterName.ExtentText -match '^\[.+\]$' ? '({1})' : '{1}' $newExtentText += " -{0} $extentTextFormat" -f $currentParameter, $commandParameters.$originalParameterName.ExtentText } } Write-Debug -Message ('Converted the command `{0}` to `{1}`.' -f $CommandAst.Extent.Text, $newExtentText) return $newExtentText } #EndRegion '.\Private\Convert-ShouldBeOfType.ps1' 228 #Region '.\Private\Convert-ShouldBeTrue.ps1' -1 <# .SYNOPSIS Converts a command `Should -BeTrue` to the specified Pester syntax. .DESCRIPTION The Convert-ShouldBeTrue function is used to convert a command `Should -BeTrue` to the specified Pester syntax. .PARAMETER CommandAst The CommandAst object representing the command to be converted. .PARAMETER Pester6 Specifies that the command should be converted to Pester version 6 syntax. .PARAMETER UseNamedParameters Specifies whether to use named parameters in the converted syntax. .PARAMETER UsePositionalParameters Specifies whether to use positional parameters in the converted syntax, where supported. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -BeTrue') Convert-ShouldBeTrue -CommandAst $commandAst -Pester6 This example converts the `Should -BeTrue` command to Pester 6 syntax. .NOTES Pester 5 Syntax: Should -BeTrue [[-ActualValue] <Object>] [[-Because] <string>] [-Not] Positional parameters: Position 1: Because Position 2: ActualValue Pester 6 Syntax: Should-BeTrue [[-Actual] <Object>] [[-Because] <String>] Positional parameters: Position 1: Actual Position 2: Because Conversion notes: If the Pester 5 syntax is negated it must be converted to Should-BeFalse. If the Pester 5 syntax uses positional parameters, the conversion must convert position 1 to position 2 and vice versa. #> function Convert-ShouldBeTrue { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter(Mandatory = $true, ParameterSetName = 'Pester6')] [System.Management.Automation.SwitchParameter] $Pester6, [Parameter()] [System.Management.Automation.SwitchParameter] $UseNamedParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $UsePositionalParameters ) $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters MutuallyExclusiveList1 = @('UseNamedParameters') MutuallyExclusiveList2 = @('UsePositionalParameters') } Assert-BoundParameter @assertBoundParameterParameters Write-Debug -Message ('Parsing the command AST: {0}' -f $CommandAst.Extent.Text) # Determine if the command is negated $isNegated = Test-PesterCommandNegated -CommandAst $CommandAst $sourceSyntaxVersion = Get-PesterCommandSyntaxVersion -CommandAst $CommandAst # Parse the command elements and convert them to Pester 6 syntax if ($PSCmdlet.ParameterSetName -eq 'Pester6') { Write-Debug -Message ('Converting from Pester v{0} to Pester v6 syntax.' -f $sourceSyntaxVersion) # Add the correct Pester command based on negation if ($isNegated) { $newExtentText = 'Should-BeFalse' } else { $newExtentText = 'Should-BeTrue' } $getPesterCommandParameterParameters = @{ CommandAst = $CommandAst CommandName = 'Should' IgnoreParameter = 'BeTrue', 'Not' PositionalParameter = 'Because', 'ActualValue' } $commandParameters = Get-PesterCommandParameter @getPesterCommandParameterParameters # Determine if named or positional parameters should be forcibly used if ($UseNamedParameters.IsPresent) { $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) } elseif ($UsePositionalParameters.IsPresent) { # First set all to named parameters $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) <# If a previous positional parameter is missing then the ones behind it cannot be set to positional. #> if ($commandParameters.ActualValue) { $commandParameters.ActualValue.Positional = $true if ($commandParameters.Because) { $commandParameters.Because.Positional = $true } } } $newExtentText += $commandParameters.ActualValue.Positional ? (' {0}' -f $commandParameters.ActualValue.ExtentText) : '' $newExtentText += $commandParameters.Because.Positional ? (' {0}' -f $commandParameters.Because.ExtentText) : '' # Holds the new parameter names so they can be added in alphabetical order. $parameterNames = @() foreach ($currentParameter in $commandParameters.Keys) { if ($commandParameters.$currentParameter.Positional -eq $true) { continue } switch ($currentParameter) { 'ActualValue' { $parameterNames += @{ Actual = 'ActualValue' } break } default { $parameterNames += @{ $currentParameter = $currentParameter } break } } } # This handles the named parameters in the command elements, added in alphabetical order. foreach ($currentParameter in $parameterNames.Keys | Sort-Object) { $originalParameterName = $parameterNames.$currentParameter $newExtentText += ' -{0} {1}' -f $currentParameter, $commandParameters.$originalParameterName.ExtentText } } Write-Debug -Message ('Converted the command `{0}` to `{1}`.' -f $CommandAst.Extent.Text, $newExtentText) return $newExtentText } #EndRegion '.\Private\Convert-ShouldBeTrue.ps1' 184 #Region '.\Private\Convert-ShouldContain.ps1' -1 <# .SYNOPSIS Converts a command `Should -Contain` to the specified Pester syntax. .DESCRIPTION The Convert-ShouldContain function is used to convert a command `Should -Contain` to the specified Pester syntax. .PARAMETER CommandAst The CommandAst object representing the command to be converted. .PARAMETER Pester6 Specifies that the command should be converted to Pester version 6 syntax. .PARAMETER UseNamedParameters Specifies whether to use named parameters in the converted syntax. .PARAMETER UsePositionalParameters Specifies whether to use positional parameters in the converted syntax, where supported. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -Contain "Test"') Convert-ShouldContain -CommandAst $commandAst -Pester6 This example converts the `Should -Contain "Test"` command to Pester 6 syntax. .NOTES Pester 5 Syntax: Should -Contain [[-ActualValue] <Object>] [[-ExpectedValue] <Object>] [[-Because] <string>] [-Not] Positional parameters: Position 1: ExpectedValue Position 2: Because Pester 6 Syntax: Should-ContainCollection [-Actual] <Object>] [-Expected] <Object> [-Because <String>] Should-NotContainCollection [[-Actual] <Object>] [-Expected] <Object> [-Because <String>] Positional parameters: Position 1: Expected Position 2: Actual #> function Convert-ShouldContain { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter(Mandatory = $true, ParameterSetName = 'Pester6')] [System.Management.Automation.SwitchParameter] $Pester6, [Parameter()] [System.Management.Automation.SwitchParameter] $UseNamedParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $UsePositionalParameters ) $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters MutuallyExclusiveList1 = @('UseNamedParameters') MutuallyExclusiveList2 = @('UsePositionalParameters') } Assert-BoundParameter @assertBoundParameterParameters Write-Debug -Message ('Parsing the command AST: {0}' -f $CommandAst.Extent.Text) # Determine if the command is negated $isNegated = Test-PesterCommandNegated -CommandAst $CommandAst $sourceSyntaxVersion = Get-PesterCommandSyntaxVersion -CommandAst $CommandAst # Parse the command elements and convert them to Pester 6 syntax if ($PSCmdlet.ParameterSetName -eq 'Pester6') { Write-Debug -Message ('Converting from Pester v{0} to Pester v6 syntax.' -f $sourceSyntaxVersion) # Add the correct Pester command based on negation if ($isNegated) { $newExtentText = 'Should-NotContainCollection' } else { $newExtentText = 'Should-ContainCollection' } $getPesterCommandParameterParameters = @{ CommandAst = $CommandAst CommandName = 'Should' IgnoreParameter = @( 'Contain' 'Not' ) PositionalParameter = @( 'ExpectedValue' 'Because' ) NamedParameter = @( 'ActualValue' ) } $commandParameters = Get-PesterCommandParameter @getPesterCommandParameterParameters # Parameter 'Because' is only supported as named parameter in Pester 6 syntax. if ($commandParameters.Because) { $commandParameters.Because.Positional = $false } # Determine if named or positional parameters should be forcibly used if ($UseNamedParameters.IsPresent) { $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) } elseif ($UsePositionalParameters.IsPresent) { # First set all to named parameters $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) <# If a previous positional parameter is missing then the ones behind it cannot be set to positional. #> if ($commandParameters.ExpectedValue) { $commandParameters.ExpectedValue.Positional = $true if ($commandParameters.ActualValue) { $commandParameters.ActualValue.Positional = $true } } } $newExtentText += $commandParameters.ExpectedValue.Positional ? (' {0}' -f $commandParameters.ExpectedValue.ExtentText) : '' $newExtentText += $commandParameters.ActualValue.Positional ? (' {0}' -f $commandParameters.ActualValue.ExtentText) : '' # Holds the new parameter names so they can be added in alphabetical order. $parameterNames = @() foreach ($currentParameter in $commandParameters.Keys) { if ($commandParameters.$currentParameter.Positional -eq $true) { continue } switch ($currentParameter) { 'ActualValue' { $parameterNames += @{ Actual = 'ActualValue' } break } 'ExpectedValue' { $parameterNames += @{ Expected = 'ExpectedValue' } break } default { $parameterNames += @{ $currentParameter = $currentParameter } break } } } # This handles the named parameters in the command elements, added in alphabetical order. foreach ($currentParameter in $parameterNames.Keys | Sort-Object) { $originalParameterName = $parameterNames.$currentParameter $newExtentText += ' -{0} {1}' -f $currentParameter, $commandParameters.$originalParameterName.ExtentText } } Write-Debug -Message ('Converted the command `{0}` to `{1}`.' -f $CommandAst.Extent.Text, $newExtentText) return $newExtentText } #EndRegion '.\Private\Convert-ShouldContain.ps1' 203 #Region '.\Private\Convert-ShouldMatch.ps1' -1 <# .SYNOPSIS Converts a command `Should -Match` to the specified Pester syntax. .DESCRIPTION The Convert-ShouldMatch function is used to convert a command `Should -Match` to the specified Pester syntax. .PARAMETER CommandAst The CommandAst object representing the command to be converted. .PARAMETER Pester6 Specifies that the command should be converted to Pester version 6 syntax. .PARAMETER UseNamedParameters Specifies whether to use named parameters in the converted syntax. .PARAMETER UsePositionalParameters Specifies whether to use positional parameters in the converted syntax, where supported. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -Match "Test"') Convert-ShouldMatch -CommandAst $commandAst -Pester6 This example converts the `Should -Match "Test"` command to Pester 6 syntax. .NOTES Pester 5 Syntax: Should -Match [[-ActualValue] <Object>] [[-RegularExpression] <Object>] [[-Because] <string>] [-Not] Positional parameters: Position 1: ExpectedValue Position 2: Because Position 3: ActualValue Pester 6 Syntax: # TODO: Update the Pester 6 syntax once it is finalized. Should-MatchString [[-Actual] <Object>] [-Expected] <Object> [-Because <String>] Should-NotMatchString [[-Actual] <Object>] [-Expected] <Object> [-Because <String>] Positional parameters: Position 1: Expected Position 2: Actual #> function Convert-ShouldMatch { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter(Mandatory = $true, ParameterSetName = 'Pester6')] [System.Management.Automation.SwitchParameter] $Pester6, [Parameter()] [System.Management.Automation.SwitchParameter] $UseNamedParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $UsePositionalParameters ) $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters MutuallyExclusiveList1 = @('UseNamedParameters') MutuallyExclusiveList2 = @('UsePositionalParameters') } Assert-BoundParameter @assertBoundParameterParameters Write-Debug -Message ('Parsing the command AST: {0}' -f $CommandAst.Extent.Text) # Determine if the command is negated $isNegated = Test-PesterCommandNegated -CommandAst $CommandAst $sourceSyntaxVersion = Get-PesterCommandSyntaxVersion -CommandAst $CommandAst # Parse the command elements and convert them to Pester 6 syntax if ($PSCmdlet.ParameterSetName -eq 'Pester6') { Write-Debug -Message ('Converting from Pester v{0} to Pester v6 syntax.' -f $sourceSyntaxVersion) # Add the correct Pester command based on negation if ($isNegated) { $newExtentText = 'Should-NotMatchString' } else { $newExtentText = 'Should-MatchString' } $getPesterCommandParameterParameters = @{ CommandAst = $CommandAst CommandName = 'Should' IgnoreParameter = @( 'Match' 'Not' ) PositionalParameter = @( 'RegularExpression' 'Because' 'ActualValue' ) } $commandParameters = Get-PesterCommandParameter @getPesterCommandParameterParameters # Parameter 'Because' is only supported as named parameter in Pester 6 syntax. if ($commandParameters.Because) { $commandParameters.Because.Positional = $false } # Determine if named or positional parameters should be forcibly used if ($UseNamedParameters.IsPresent) { $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) } elseif ($UsePositionalParameters.IsPresent) { # First set all to named parameters $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) <# If a previous positional parameter is missing then the ones behind it cannot be set to positional. #> if ($commandParameters.RegularExpression) { $commandParameters.RegularExpression.Positional = $true if ($commandParameters.ActualValue) { $commandParameters.ActualValue.Positional = $true } } } $newExtentText += $commandParameters.RegularExpression.Positional ? (' {0}' -f $commandParameters.RegularExpression.ExtentText) : '' $newExtentText += $commandParameters.ActualValue.Positional ? (' {0}' -f $commandParameters.ActualValue.ExtentText) : '' # Holds the new parameter names so they can be added in alphabetical order. $parameterNames = @() foreach ($currentParameter in $commandParameters.Keys) { if ($commandParameters.$currentParameter.Positional -eq $true) { continue } switch ($currentParameter) { 'ActualValue' { $parameterNames += @{ Actual = 'ActualValue' } break } 'RegularExpression' { $parameterNames += @{ Expected = 'RegularExpression' } break } default { $parameterNames += @{ $currentParameter = $currentParameter } break } } } # This handles the named parameters in the command elements, added in alphabetical order. foreach ($currentParameter in $parameterNames.Keys | Sort-Object) { $originalParameterName = $parameterNames.$currentParameter $newExtentText += ' -{0} {1}' -f $currentParameter, $commandParameters.$originalParameterName.ExtentText } } Write-Debug -Message ('Converted the command `{0}` to `{1}`.' -f $CommandAst.Extent.Text, $newExtentText) return $newExtentText } #EndRegion '.\Private\Convert-ShouldMatch.ps1' 203 #Region '.\Private\Convert-ShouldMatchExactly.ps1' -1 <# .SYNOPSIS Converts a command `Should -MatchExactly` to the specified Pester syntax. .DESCRIPTION The Convert-ShouldBe function is used to convert a command `Should -MatchExactly` to the specified Pester syntax. .PARAMETER CommandAst The CommandAst object representing the command to be converted. .PARAMETER Pester6 Specifies that the command should be converted to Pester version 6 syntax. .PARAMETER UseNamedParameters Specifies whether to use named parameters in the converted syntax. .PARAMETER UsePositionalParameters Specifies whether to use positional parameters in the converted syntax, where supported. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -MatchExactly "Test"') Convert-ShouldMatchExactly -CommandAst $commandAst -Pester6 This example converts the `Should -MatchExactly "Test"` command to Pester 6 syntax. .NOTES Pester 5 Syntax: Should -MatchExactly [[-ActualValue] <Object>] [-Not] [[-ExpectedValue] <Object>] [-Because <Object>] Positional parameters: Position 1: ExpectedValue Position 2: Because Position 3: ActualValue Pester 6 Syntax: Should-BeString [[-Actual] <Object>] [[-Expected] <String>] [-Because <String>] [-CaseSensitive] [-IgnoreWhitespace] Should-NotBeString [[-Actual] <Object>] [[-Expected] <String>] [-Because <String>] [-CaseSensitive] [-IgnoreWhitespace] Positional parameters: Position 1: Expected Position 2: Actual #> function Convert-ShouldMatchExactly { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter(Mandatory = $true, ParameterSetName = 'Pester6')] [System.Management.Automation.SwitchParameter] $Pester6, [Parameter()] [System.Management.Automation.SwitchParameter] $UseNamedParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $UsePositionalParameters ) $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters MutuallyExclusiveList1 = @('UseNamedParameters') MutuallyExclusiveList2 = @('UsePositionalParameters') } Assert-BoundParameter @assertBoundParameterParameters Write-Debug -Message ('Parsing the command AST: {0}' -f $CommandAst.Extent.Text) # Determine if the command is negated $isNegated = Test-PesterCommandNegated -CommandAst $CommandAst $sourceSyntaxVersion = Get-PesterCommandSyntaxVersion -CommandAst $CommandAst # Parse the command elements and convert them to Pester 6 syntax if ($PSCmdlet.ParameterSetName -eq 'Pester6') { Write-Debug -Message ('Converting from Pester v{0} to Pester v6 syntax.' -f $sourceSyntaxVersion) # Add the correct Pester command based on negation if ($isNegated) { $newExtentText = 'Should-NotMatchString' } else { $newExtentText = 'Should-MatchString' } # Always add the `-CaseSensitive` parameter since MatchExactly was case-sensitive. $newExtentText += ' -CaseSensitive' $getPesterCommandParameterParameters = @{ CommandAst = $CommandAst CommandName = 'Should' IgnoreParameter = @( 'CMATCH' 'MatchExactly' 'Not' ) PositionalParameter = @( 'RegularExpression' 'Because' 'ActualValue' ) } $commandParameters = Get-PesterCommandParameter @getPesterCommandParameterParameters # Parameter 'Because' is only supported as named parameter in Pester 6 syntax. if ($commandParameters.Because) { $commandParameters.Because.Positional = $false } # Determine if named or positional parameters should be forcibly used if ($UseNamedParameters.IsPresent) { $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) } elseif ($UsePositionalParameters.IsPresent) { # First set all to named parameters $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) <# If a previous positional parameter is missing then the ones behind it cannot be set to positional. #> if ($commandParameters.RegularExpression) { $commandParameters.RegularExpression.Positional = $true if ($commandParameters.ActualValue) { $commandParameters.ActualValue.Positional = $true } } } $newExtentText += $commandParameters.RegularExpression.Positional ? (' {0}' -f $commandParameters.RegularExpression.ExtentText) : '' $newExtentText += $commandParameters.ActualValue.Positional ? (' {0}' -f $commandParameters.ActualValue.ExtentText) : '' # Holds the new parameter names so they can be added in alphabetical order. $parameterNames = @() foreach ($currentParameter in $commandParameters.Keys) { if ($commandParameters.$currentParameter.Positional -eq $true) { continue } switch ($currentParameter) { 'ActualValue' { $parameterNames += @{ Actual = 'ActualValue' } break } 'RegularExpression' { $parameterNames += @{ Expected = 'RegularExpression' } break } default { $parameterNames += @{ $currentParameter = $currentParameter } break } } } # This handles the named parameters in the command elements, added in alphabetical order. foreach ($currentParameter in $parameterNames.Keys | Sort-Object) { $originalParameterName = $parameterNames.$currentParameter $newExtentText += ' -{0} {1}' -f $currentParameter, $commandParameters.$originalParameterName.ExtentText } } Write-Debug -Message ('Converted the command `{0}` to `{1}`.' -f $CommandAst.Extent.Text, $newExtentText) return $newExtentText } #EndRegion '.\Private\Convert-ShouldMatchExactly.ps1' 206 #Region '.\Private\Convert-ShouldNotThrow.ps1' -1 <# .SYNOPSIS Converts a command `Should -Not -Throw` to the specified Pester syntax. .DESCRIPTION The Convert-ShouldBe function is used to convert a command `Should -Not -Throw` to the specified Pester syntax. .PARAMETER CommandAst The CommandAst object representing the command to be converted. .PARAMETER Pester6 Specifies that the command should be converted to Pester version 6 syntax. .PARAMETER UseNamedParameters Specifies whether to use named parameters in the converted syntax. .PARAMETER UsePositionalParameters Specifies whether to use positional parameters in the converted syntax, where supported. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -Not -Throw') Convert-ShouldThrow -CommandAst $commandAst -Pester6 This example converts the `Should -Not -Throw` command to Pester 6 syntax. .NOTES Pester 5 Syntax: Should -Throw [[-ActualValue] <Object>] [[-ExpectedMessage] <string>] [[-ErrorId] <string>] [[-ExceptionType] <type>] [[-Because] <string>] [-Not] [-PassThru] Positional parameters: Position 1: ExceptionMessage Position 2: ErrorId Position 3: ExceptionType Position 4: Because Pester 6 Syntax: There are no Should-* command to call in v6. In v6 the scriptblock should be called either directly or using the call operator, e.g: $null = & (<ActualValue>) Conversion notes: From Frode: "$null = & (<actualvalue>) should work for variables, script blocks and expressions (note parentheses). Running the code directly will send data to StandardOutput in the Test-object. Not a big deal unless there's a lot of output, but might as well assign it to null like -Throw." #> function Convert-ShouldNotThrow { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter(Mandatory = $true, ParameterSetName = 'Pester6')] [System.Management.Automation.SwitchParameter] $Pester6, [Parameter()] [System.Management.Automation.SwitchParameter] $UseNamedParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $UsePositionalParameters ) $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters MutuallyExclusiveList1 = @('UseNamedParameters') MutuallyExclusiveList2 = @('UsePositionalParameters') } Assert-BoundParameter @assertBoundParameterParameters Write-Debug -Message ('Parsing the command AST: {0}' -f $CommandAst.Extent.Text) # Determine if the command is negated $isNegated = Test-PesterCommandNegated -CommandAst $CommandAst $sourceSyntaxVersion = Get-PesterCommandSyntaxVersion -CommandAst $CommandAst # Parse the command elements and convert them to Pester 6 syntax if ($PSCmdlet.ParameterSetName -eq 'Pester6') { Write-Debug -Message ('Converting from Pester v{0} to Pester v6 syntax.' -f $sourceSyntaxVersion) if ($isNegated) { <# Must extract the scriptblock from the CommandAst extent, the scriptblock is passed as the parameter ActualValue or passed thru the pipeline. #> $newExtentText = '$null = & ({0})' -f (Get-ShouldThrowScriptBlock -CommandAst $CommandAst -ParameterName 'ActualValue' -ParsePipeline) } else { #$shouldNotThrowNotImplementedMessage = $script:localizedData.ShouldNotThrow_NotImplemented $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( 'Convert-ShouldNotThrow should not be called without a negation parameter. Call Convert-ShouldThrow instead.', #$shouldNotThrowNotImplementedMessage, 'CSNT0001', # cspell: disable-line [System.Management.Automation.ErrorCategory]::NotImplemented, $CommandAst.Extent.Text ) ) } } Write-Debug -Message ('Converted the command `{0}` to `{1}`.' -f $CommandAst.Extent.Text, $newExtentText) return $newExtentText } #EndRegion '.\Private\Convert-ShouldNotThrow.ps1' 121 #Region '.\Private\Convert-ShouldThrow.ps1' -1 <# .SYNOPSIS Converts a command `Should -Throw` to the specified Pester syntax. .DESCRIPTION The Convert-ShouldBe function is used to convert a command `Should -Throw` to the specified Pester syntax. .PARAMETER CommandAst The CommandAst object representing the command to be converted. .PARAMETER Pester6 Specifies that the command should be converted to Pester version 6 syntax. .PARAMETER UseNamedParameters Specifies whether to use named parameters in the converted syntax. .PARAMETER UsePositionalParameters Specifies whether to use positional parameters in the converted syntax, where supported. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -Throw "Test"') Convert-ShouldThrow -CommandAst $commandAst -Pester6 This example converts the `Should -Throw "Test"` command to Pester 6 syntax. .NOTES Pester 5 Syntax: Should -Throw [[-ActualValue] <Object>] [[-ExpectedMessage] <string>] [[-ErrorId] <string>] [[-ExceptionType] <type>] [[-Because] <string>] [-Not] [-PassThru] Positional parameters: Position 1: ExceptionMessage Position 2: ErrorId Position 3: ExceptionType Position 4: Because Pester 6 Syntax: Should-Throw [-ScriptBlock] <ScriptBlock> [[-ExceptionType] <Type>] [[-ExceptionMessage] <String>] [[-FullyQualifiedErrorId] <String>] [-AllowNonTerminatingError] [[-Because] <String>] Positional parameters: Position 1: ExceptionMessage Position 2: FullyQualifiedErrorId Position 3: ExceptionType Position 4: Because Conversion notes: PassThru is the default in Pester 6, so it can be ignored in the conversion. But must be handled if it can have negative impact were it was not used before. TODO: Verify the positional parameters in next v6 alpha, and that they are only the four. The code below assume they will be the same as v5 in a future v6 alpha. #> function Convert-ShouldThrow { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter(Mandatory = $true, ParameterSetName = 'Pester6')] [System.Management.Automation.SwitchParameter] $Pester6, [Parameter()] [System.Management.Automation.SwitchParameter] $UseNamedParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $UsePositionalParameters ) $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters MutuallyExclusiveList1 = @('UseNamedParameters') MutuallyExclusiveList2 = @('UsePositionalParameters') } Assert-BoundParameter @assertBoundParameterParameters Write-Debug -Message ('Parsing the command AST: {0}' -f $CommandAst.Extent.Text) # Determine if the command is negated $isNegated = Test-PesterCommandNegated -CommandAst $CommandAst $sourceSyntaxVersion = Get-PesterCommandSyntaxVersion -CommandAst $CommandAst # Parse the command elements and convert them to Pester 6 syntax if ($PSCmdlet.ParameterSetName -eq 'Pester6') { Write-Debug -Message ('Converting from Pester v{0} to Pester v6 syntax.' -f $sourceSyntaxVersion) # Add the correct Pester command based on negation if ($isNegated) { #$shouldThrowNotImplementedMessage = $script:localizedData.ShouldThrow_NotImplemented $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( 'Convert-ShouldThrow should not be called with a negation parameter. Call Convert-ShouldNotThrow instead.', #$shouldThrowNotImplementedMessage, 'CST0001', # cspell: disable-line [System.Management.Automation.ErrorCategory]::NotImplemented, $CommandAst.Extent.Text ) ) } else { $newExtentText = 'Should-Throw' } $getPesterCommandParameterParameters = @{ CommandAst = $CommandAst CommandName = 'Should' IgnoreParameter = @( 'Throw' 'Not' 'PassThru' ) PositionalParameter = @( 'ExceptionMessage' 'ErrorId' 'ExceptionType' 'Because' ) NamedParameter = @( 'ActualValue' ) } $commandParameters = Get-PesterCommandParameter @getPesterCommandParameterParameters # Determine if named or positional parameters should be forcibly used if ($UseNamedParameters.IsPresent) { $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) } elseif ($UsePositionalParameters.IsPresent) { # First set all to named parameters $commandParameters.Keys.ForEach({ $commandParameters.$_.Positional = $false }) <# If a previous positional parameter is missing then the ones behind it cannot be set to positional. #> if ($commandParameters.ExceptionMessage) { $commandParameters.ExceptionMessage.Positional = $true if ($commandParameters.ErrorId) { $commandParameters.ErrorId.Positional = $true if ($commandParameters.ExceptionType) { $commandParameters.ExceptionType.Positional = $true if ($commandParameters.Because) { $commandParameters.Because.Positional = $true } } } } } $newExtentText += $commandParameters.ExceptionMessage.Positional ? (' {0}' -f $commandParameters.ExceptionMessage.ExtentText) : '' $newExtentText += $commandParameters.ErrorId.Positional ? (' {0}' -f $commandParameters.ErrorId.ExtentText) : '' $newExtentText += $commandParameters.ExceptionType.Positional ? (' {0}' -f $commandParameters.ExceptionType.ExtentText) : '' $newExtentText += $commandParameters.Because.Positional ? (' {0}' -f $commandParameters.Because.ExtentText) : '' # Holds the new parameter names so they can be added in alphabetical order. $parameterNames = @() foreach ($currentParameter in $commandParameters.Keys) { if ($commandParameters.$currentParameter.Positional -eq $true) { continue } switch ($currentParameter) { 'ErrorId' { $parameterNames += @{ FullyQualifiedErrorId = 'ErrorId' } break } 'ActualValue' { $parameterNames += @{ ScriptBlock = 'ActualValue' } break } default { $parameterNames += @{ $currentParameter = $currentParameter } break } } } # This handles the named parameters in the command elements, added in alphabetical order. foreach ($currentParameter in $parameterNames.Keys | Sort-Object) { $originalParameterName = $parameterNames.$currentParameter $newExtentText += ' -{0} {1}' -f $currentParameter, $commandParameters.$originalParameterName.ExtentText } } Write-Debug -Message ('Converted the command `{0}` to `{1}`.' -f $CommandAst.Extent.Text, $newExtentText) return $newExtentText } #EndRegion '.\Private\Convert-ShouldThrow.ps1' 233 #Region '.\Private\Get-AstDefinition.ps1' -1 <# .SYNOPSIS Retrieves the ScriptBlockAst definition of a PowerShell script file. .DESCRIPTION The Get-AstDefinition function parses a PowerShell script file and retrieves the ScriptBlockAst definition. It uses the Parser.ParseFile method from the System.Management.Automation.Language namespace to parse the file. .PARAMETER Path Specifies the path to the PowerShell script file(s) to parse. This parameter supports pipeline input and accepts both strings and FileInfo objects. .OUTPUTS System.Management.Automation.Language.ScriptBlockAst The ScriptBlockAst definition of the parsed PowerShell script file. .EXAMPLE Get-AstDefinition -Path 'C:\Scripts\Script.ps1' Retrieves the ScriptBlockAst definition of the 'Script.ps1' file. .EXAMPLE 'C:\Scripts\Script.ps1' | Get-AstDefinition Retrieves the ScriptBlockAst definition of the 'Script.ps1' file using pipeline input. .EXAMPLE Get-ChildItem -Path './scripts' | Get-AstDefinition Retrieves the ScriptBlockAst definition of all the files in the path pass as pipeline input. #> function Get-AstDefinition { [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidMultipleTypeAttributes', '', Justification = 'We want to pass in both strings and FileInfo objects to parameter Path.')] [CmdletBinding()] [OutputType([System.Management.Automation.Language.ScriptBlockAst])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [System.String[]] [System.IO.FileInfo[]] $Path ) process { foreach ($filePath in $Path) { $tokens, $parseErrors = $null Write-Verbose -Message "Parsing the script file: $filePath" [System.Management.Automation.Language.Parser]::ParseFile($filePath, [ref] $tokens, [ref] $parseErrors) if ($parseErrors) { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ($script:localizedData.FailedParseScriptAst -f $parseErrors[0].Message), 'GAD0001', # cSpell: disable-line [System.Management.Automation.ErrorCategory]::InvalidOperation, $filePath ) ) } } } } #EndRegion '.\Private\Get-AstDefinition.ps1' 74 #Region '.\Private\Get-CommandAst.ps1' -1 <# .SYNOPSIS Retrieves the abstract syntax trees (AST's) of a PowerShell command. .DESCRIPTION The Get-CommandAst function retrieves the abstract syntax tree (AST) of a PowerShell command. The AST represents the structure of the command and can be used for further analysis or manipulation. .PARAMETER Ast Specifies the AST of a script block. .PARAMETER CommandName Specifies the PowerShell command for which to retrieve the AST. .EXAMPLE Get-CommandAst -Command 'Should' This example retrieves the AST of the 'Should' command. .INPUTS [System.Collections.Generic.IEnumerable`1[System.Management.Automation.Language.Ast]] The AST of a script block. .OUTPUTS System.Management.Automation.Language.CommandAst[] The function returns the abstract syntax trees (AST's) of the specified PowerShell command. #> function Get-CommandAst { [CmdletBinding()] [OutputType([System.Collections.Generic.IEnumerable`1[System.Management.Automation.Language.Ast]])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [System.Management.Automation.Language.Ast] $Ast, [Parameter(Mandatory = $true)] [System.String] $CommandName ) process { Write-Verbose -Message "Retrieving the AST of the command: $CommandName" $commandAsts = $Ast.FindAll({ param ( [Parameter()] $node ) return $node -is [System.Management.Automation.Language.CommandAst] -and $node.GetCommandName() -eq $CommandName }, $true) return $commandAsts } } #EndRegion '.\Private\Get-CommandAst.ps1' 63 #Region '.\Private\Get-PesterCommandParameter.ps1' -1 <# .SYNOPSIS Get all the parameters of a Pester command. .DESCRIPTION The Get-PesterCommandParameter function returns all of parameter command elements. .PARAMETER CommandAst Specifies the CommandAst object representing the command. .PARAMETER CommandName Specifies the name of the command. .PARAMETER IgnoreParameter Specifies all the parameters that should be ignored. .PARAMETER NamedParameter Specifies all the names of the parameters that are not positional, that have a value associated with them. .PARAMETER PositionalParameter Specifies all the names of the positional parameters. Must be specified in the order of their numeric position. .OUTPUTS System.Collections.Hashtable Holds all the parameters of the CommandAst. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -Be "ExpectedString" "BecauseString" "ActualString"') Get-PesterCommandParameter -CommandAst $commandAst -IgnoreParameter 'Be', 'Not' -PositionalParameter 'ExpectedValue', 'Because', 'ActualValue' Returns a hashtable with the parameters. #> function Get-PesterCommandParameter { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter(Mandatory = $true)] [System.String] $CommandName, [Parameter()] [System.String[]] $IgnoreParameter = @(), [Parameter()] [System.String[]] $NamedParameter = @(), [Parameter()] [System.String[]] $PositionalParameter = @() ) process { Write-Debug -Message "Retrieving the parameters of the extent: $($CommandAst.Extent.Text)" Write-Debug -Message "Command name: $CommandName" # Filter out the command name from the command elements. $commandElement = $CommandAst.CommandElements | Where-Object -FilterScript { -not ( $_ -is [System.Management.Automation.Language.StringConstantExpressionAst] ` -and $_.Extent.Text -eq $CommandName ) } Write-Debug -Message "Ignoring the parameters: $($IgnoreParameter -join ', ')" <# Filter out the parameters to ignore from the command elements, e.g.: - Be - Not #> $commandElement = $commandElement | Where-Object -FilterScript { -not ( $_ -is [System.Management.Automation.Language.CommandParameterAst] ` -and $_.ParameterName -in $IgnoreParameter ) } # Build a hashtable based on the values in $PositionalParameter and $NamedParameter. $positionalParameterHashtable = @{} if ($commandElement.Count -gt 0) { Write-Debug -Message "Named parameters: $($NamedParameter -join ', ')" <# Filter out the value parameters including its values from the command elements, e.g.: - ActualValue - ExpectedValue - Because #> $parameterElements = $commandElement.Where({ $_ -is [System.Management.Automation.Language.CommandParameterAst] -and ($_.ParameterName -in $PositionalParameter -or $_.ParameterName -in $NamedParameter) }) $filterCommandElements = @() foreach ($parameterElement in $parameterElements) { $parameterIndex = $commandElement.IndexOf($parameterElement) # Above returned -1 if parameter name was not found. if ($parameterIndex -ne -1) { $parameterName = $commandElement[$parameterIndex].ParameterName $positionalParameterHashtable.$parameterName = @{ Position = 0 Positional = $false ExtentText = $commandElement[$parameterIndex + 1].Extent.Text } $filterCommandElements += $commandElement[$parameterIndex] $filterCommandElements += $commandElement[$parameterIndex + 1] } } # Filter out the value parameter and its value from the command elements. $commandElement = $commandElement | Where-Object -FilterScript { $_ -notin $filterCommandElements } } # Get the positional parameters extent text that are left (if any). if ($commandElement.Count -gt 0) { Write-Debug -Message "Positional parameters: $($PositionalParameter -join ', ')" $elementCounter = 0 $positionalCounter = 1 foreach ($parameter in $PositionalParameter) { # Only add the positional parameter if it does not exist in the hashtable. if (-not $positionalParameterHashtable.ContainsKey($parameter)) { # Only add positional parameter if there actually a value for it. $positionalParameterHashtable.$parameter = @{ Position = $positionalCounter Positional = $true ExtentText = $commandElement[$elementCounter].Extent.Text #? $commandElement.Extent.Text : $null } # Increment the positional counter. $elementCounter++ # If the command element is $null then there are no more positional parameters to process. if ($null -eq $commandElement[$elementCounter]) { break } } # Increment the positional counter. $positionalCounter++ } } return $positionalParameterHashtable } } #EndRegion '.\Private\Get-PesterCommandParameter.ps1' 173 #Region '.\Private\Get-PesterCommandSyntaxVersion.ps1' -1 <# .SYNOPSIS Determines the Pester command syntax version based on the command and parameter name. .DESCRIPTION This function checks the command and parameter name used in a Pester command to determine the syntax version of the command. It supports identifying syntax versions based on specific commands and parameters. .PARAMETER CommandAst The abstract syntax tree of the command being analyzed. .PARAMETER CommandName The name of the command to check for in the AST. This allows for dynamic checking of different commands. .PARAMETER ParameterName The name of the parameter to check for in the command. This allows for dynamic checking of different parameters. .EXAMPLE $ast = [System.Management.Automation.Language.Parser]::ParseInput('Should -BeExactly "value"', [ref]$null, [ref]$null) Get-PesterCommandSyntaxVersion -CommandAst $ast -CommandName 'Should' -ParameterName 'BeExactly' Returns the syntax version for the 'Should -BeExactly' command. #> function Get-PesterCommandSyntaxVersion { [CmdletBinding()] [OutputType([System.Int32])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst ) $sourceSyntaxVersion = $null if ($CommandAst.CommandElements[0].Extent.Text -match 'Should-\w+\b') { $sourceSyntaxVersion = 6 } elseif ($CommandAst.CommandElements[0].Extent.Text -eq 'Should' -and (Get-ShouldCommandOperatorName -CommandAst $CommandAst)) { $sourceSyntaxVersion = 5 } return $sourceSyntaxVersion } #EndRegion '.\Private\Get-PesterCommandSyntaxVersion.ps1' 52 #Region '.\Private\Get-ShouldCommandOperatorName.ps1' -1 <# .SYNOPSIS Retrieves the Should command operator based on the provided CommandAst. .DESCRIPTION The Get-ShouldCommandOperatorName function retrieves the Should command operator based on the provided CommandAst object. It searches for specific operators and their aliases in the CommandAst and returns the corresponding operator name. .PARAMETER CommandAst Specifies the CommandAst object representing the command for which the Should operator needs to be retrieved. .OUTPUTS System.String The Should command operator name. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -Be "Hello"') Get-ShouldCommandOperatorName -CommandAst $commandAst Returns "Be" #> function Get-ShouldCommandOperatorName { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst ) process { # Operators. Output from Pester's command Get-ShouldOperator. $possibleShouldOperator = @( 'Be', 'BeExactly', 'BeGreaterThan', 'BeLessOrEqual', 'BeIn', 'BeLessThan', 'BeGreaterOrEqual', 'BeLike', 'BeLikeExactly', 'BeNullOrEmpty', 'BeOfType', 'BeTrue', 'BeFalse', 'Contain', 'Exist', 'FileContentMatch', 'FileContentMatchExactly', 'FileContentMatchMultiline', 'FileContentMatchMultilineExactly', 'HaveCount', 'HaveParameter', 'Match', 'MatchExactly', 'Throw', 'InvokeVerifiable', 'Invoke' ) # Operator aliases. Output from Pester's command Get-ShouldOperator. $possibleShouldOperatorAlias = @{ 'EQ' = 'Be' 'CEQ' = 'BeExactly' 'GT' = 'BeGreaterThan' 'LE' = 'BeLessOrEqual' 'LT' = 'BeLessThan' 'GE' = 'BeGreaterOrEqual' 'HaveType' = 'BeOfType' 'CMATCH' = 'MatchExactly' } $shouldOperatorAsts = $CommandAst.Find({ param ( [Parameter()] $node ) return $node -is [System.Management.Automation.Language.CommandParameterAst] -and ($node.ParameterName -in $possibleShouldOperator -or $node.ParameterName -in $possibleShouldOperatorAlias.Keys) }, $true) if ($shouldOperatorAsts.ParameterName -in $possibleShouldOperatorAlias.Keys) { return $possibleShouldOperatorAlias[$shouldOperatorAsts.ParameterName] } return $shouldOperatorAsts.ParameterName } } #EndRegion '.\Private\Get-ShouldCommandOperatorName.ps1' 99 #Region '.\Private\Get-ShouldThrowScriptBlock.ps1' -1 <# .SYNOPSIS Retrieves the script block associated with a pipeline or parameter. .DESCRIPTION The Get-ShouldThrowScriptBlock function is used to retrieve the script block associated with a pipeline or parameter. It can be used to extract the script block from a pipeline or from a specific parameter. .PARAMETER CommandAst The CommandAst parameter specifies the command AST (Abstract Syntax Tree) object from which the script block is to be retrieved. .PARAMETER ParameterName Specifies the name of the parameter whose script block is to be retrieved. .PARAMETER ParsePipeline Specifies whether to parse the pipeline to find the script block. .OUTPUTS System.String The function returns the text of the script block if found, otherwise it returns $null. .EXAMPLE Get-ShouldThrowScriptBlock -CommandAst $commandAst -ParameterName 'ActualValue' -ParsePipeline Retrieves the script block associated with the specified command AST and parameter name, parsing the pipeline if necessary. #> function Get-ShouldThrowScriptBlock { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst, [Parameter()] [System.String] $ParameterName, [Parameter()] [System.Management.Automation.SwitchParameter] $ParsePipeline ) process { # Initialize the scriptblock variable $scriptBlock = $null # If scriptblock is not found in the pipeline, look for it in the parameters if ($ParameterName) { # Find the parameter by name $commandParameterAst = $CommandAst.CommandElements | Where-Object -FilterScript { $_ -is [System.Management.Automation.Language.CommandParameterAst] -and $_.ParameterName -eq $ParameterName } if ($commandParameterAst) { $commandParameterIndex = $commandParameterAst.Parent.CommandElements.IndexOf($commandParameterAst) # Assuming the next element is the argument to the parameter $argumentAst = $commandParameterAst.Parent.CommandElements[$commandParameterIndex + 1] # Retrieve the argument $scriptBlock = $argumentAst.Extent.Text # if ($argumentAst -and $argumentAst -is [System.Management.Automation.Language.ScriptBlockExpressionAst]) # { # $scriptBlock = $argumentAst.ScriptBlock # } } } # Check if we need to parse the pipeline if (-not $scriptBlock -and $ParsePipeline.IsPresent) { # Attempt to find a pipeline before the CommandAst in the script $pipelineAst = $CommandAst.Parent # Only get the scriptblock if the pipeline has more than one element if ($pipelineAst -is [System.Management.Automation.Language.PipelineAst] -and $pipelineAst.PipelineElements.Count -gt 1) { # If a pipeline is found, get all the pipeline elements except the one that is the CommandAst $lastPipelineElement = $pipelineAst.PipelineElements[-1] $scriptBlock = $pipelineAst.Extent.Text.Replace($lastPipelineElement.Extent.Text, '').TrimEnd(" |`r`n") } } # Return the scriptblock's text if found, otherwise return null return $scriptBlock } } #EndRegion '.\Private\Get-ShouldThrowScriptBlock.ps1' 101 #Region '.\Private\Test-PesterCommandNegated.ps1' -1 <# .SYNOPSIS Determines if a Pester command is negated. .DESCRIPTION The Test-PesterCommandNegated function is used to determine if a Pester command is negated. It searches for the 'Not' parameter in the CommandAst and also handles the scenario where the 'Not' parameter is specifically set to $false. .PARAMETER CommandAst The CommandAst object representing the Pester command. .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -Not -Be "Test"') Test-PesterCommandNegated -CommandAst $commandAst Returns $true .EXAMPLE $commandAst = [System.Management.Automation.Language.Parser]::ParseInput('Should -Be "Test"') Test-PesterCommandNegated -CommandAst $commandAst Returns $false .INPUTS System.Management.Automation.Language.CommandAst .OUTPUTS System.Boolean #> function Test-PesterCommandNegated { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Language.CommandAst] $CommandAst ) if ($CommandAst.Parent -is [System.Management.Automation.Language.PipelineAst]) { # Assuming the last element in the pipeline is the command we are interested in. $CommandAst = $CommandAst.Parent.PipelineElements[-1] } $negateCommandParameterAst = $CommandAst.CommandElements | Where-Object -FilterScript { $_ -is [System.Management.Automation.Language.CommandParameterAst] ` -and $_.ParameterName -eq 'Not' ` -and $_.Argument.Extent.Text -ne '$false' } $negated = $false if ($negateCommandParameterAst) { $negated = $true } return $negated } #EndRegion '.\Private\Test-PesterCommandNegated.ps1' 65 #Region '.\Public\Convert-PesterSyntax.ps1' -1 <# .SYNOPSIS Converts the syntax of a file to the syntax of a newer Pester version. .DESCRIPTION The Convert-PesterSyntax command is used to convert the syntax of a file to the syntax of a newer Pester version.. It supports converting to Pester 6 format. .PARAMETER Path Specifies the path of the file(s) to be converted. This parameter is mandatory and accepts a string or a FileInfo object. .PARAMETER Pester6 Specifies that the syntax to convert to is Pester 6. This parameter is mandatory to convert to Pester 6 syntax. .PARAMETER UseNamedParameters Specifies whether to use named parameters in the converted syntax. .PARAMETER UsePositionalParameters Specifies whether to use positional parameters in the converted syntax, where supported. .PARAMETER Force Specifies that the file should be created without any confirmation. .PARAMETER PassThru Returns the script after converting the syntax. This parameter is most useful when passing in a single file to convert. If multiple files are passed in, the script of all the files will be returned as an array. If PassThru is specified, no file will not be modified. .EXAMPLE Convert-PesterSyntax -Path "C:\Scripts\Test.ps1" -Pester6 Converts the syntax of the Test.ps1 file to Pester 6 syntax. .EXAMPLE Get-ChildItem -Path "C:\Scripts" -Recurse -Filter "*.ps1" | Convert-PesterSyntax Converts the syntax of all PowerShell files in the C:\Scripts directory and its subdirectories to the default (newest) Pester syntax. #> function Convert-PesterSyntax { [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidMultipleTypeAttributes', '', Justification = 'We want to pass in both strings and FileInfo objects to parameter Path.')] [CmdletBinding(DefaultParameterSetName = 'Pester6', SupportsShouldProcess = $true, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [System.String[]] [System.IO.FileInfo[]] $Path, [Parameter(ParameterSetName = 'Pester6')] [System.Management.Automation.SwitchParameter] $Pester6, [Parameter()] [System.Management.Automation.SwitchParameter] $UseNamedParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $UsePositionalParameters, [Parameter()] [System.Management.Automation.SwitchParameter] $Force, [Parameter()] [System.Management.Automation.SwitchParameter] $PassThru ) begin { if (($Force.IsPresent -and -not $Confirm) -or $PassThru.IsPresent) { $ConfirmPreference = 'None' } $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters MutuallyExclusiveList1 = @('UseNamedParameters') MutuallyExclusiveList2 = @('UsePositionalParameters') } Assert-BoundParameter @assertBoundParameterParameters $convertParameters = @{} + $PSBoundParameters $convertParameters.Remove('Path') $convertParameters.Remove('Force') $convertParameters.Remove('PassThru') if ($PSCmdlet.ParameterSetName -eq 'Pester6' -and -not $Pester6.IsPresent) { $Pester6 = $true $convertParameters.Pester6 = $true } if ($Pester6) { Write-Verbose 'Converting to Pester 6 syntax.' } else { throw 'No version syntax specified. Please specify a format to convert to.' } } process { if ($Path.Count -gt 1) { Write-Progress -Id 1 -Activity 'Converting Pester syntax' -Status ('Processing {0} file(s)' -f $Path.Count) -PercentComplete 0 } else { Write-Progress -Id 1 -Activity 'Converting Pester syntax' -Status ('Processing file {0}' -f (Split-Path -Path $Path -Leaf)) -PercentComplete 0 } foreach ($filePath in $Path) { if ($Path.Count -gt 1) { Write-Progress -Id 1 -Activity 'Converting Pester syntax' -Status "Processing $filePath" -PercentComplete (($Path.IndexOf($filePath) / $Path.Count) * 100) } $verboseDescriptionMessage = $script:localizedData.Convert_PesterSyntax_ShouldProcessVerboseDescription -f $filePath $verboseWarningMessage = $script:localizedData.Convert_PesterSyntax_ShouldProcessVerboseWarning -f $filePath $captionMessage = $script:localizedData.Convert_PesterSyntax_ShouldProcessCaption if (-not ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage))) { continue } if ($filePath -is [System.String]) { $filePath = Convert-Path -Path $filePath } $scriptBlockAst = $filePath | Get-AstDefinition -ErrorAction 'Stop' # Get the script text from the script block AST that will be used to replace the original script text. $convertedScriptText = $scriptBlockAst.Extent.Text Write-Debug -Message ('Parsing the script block AST: {0}' -f $scriptBlockAst.Extent.Text) <# Get all the Should command AST's in the script block AST, and sort them by their start offset in descending order. The descending order is so that we can replace the original extent text with the new extent without reloading the script block AST. #> $shouldCommandAst = $scriptBlockAst | Get-CommandAst -CommandName 'Should' -ErrorAction 'Stop' | Sort-Object -Property { $_.Extent.StartOffset } -Descending if ($shouldCommandAst) { Write-Progress -Id 2 -ParentId 1 -Activity 'Converting Should command syntax' -Status ('Processing {0} command(s)' -f $shouldCommandAst.Count) -PercentComplete 0 Write-Debug -Message ('Found {0} ''Should'' command(s) in {1}.' -f $shouldCommandAst.Count, $filePath) foreach ($commandAst in $shouldCommandAst) { $apply = $true # Get start and end offsets of commandAst.Extent $startOffset = $commandAst.Extent.StartOffset $endOffset = $commandAst.Extent.EndOffset # If only one item was returned then there is no collection that has the method IndexOf. $percentComplete = $shouldCommandAst.Count -gt 1 ? (($shouldCommandAst.IndexOf($commandAst) / $shouldCommandAst.Count) * 100) : 100 Write-Progress -Id 2 -ParentId 1 -Activity 'Converting Should command syntax' -Status "Processing extent on line $($commandAst.Extent.StartLineNumber)" -PercentComplete $percentComplete $operatorName = Get-ShouldCommandOperatorName -CommandAst $commandAst -ErrorAction 'Stop' if ($operatorName) { switch ($operatorName) { 'Be' { $newExtentText = Convert-ShouldBe -CommandAst $commandAst @convertParameters -ErrorAction 'Stop' break } 'BeExactly' { $newExtentText = Convert-ShouldBeExactly -CommandAst $commandAst @convertParameters -ErrorAction 'Stop' break } 'BeTrue' { $newExtentText = Convert-ShouldBeTrue -CommandAst $commandAst @convertParameters -ErrorAction 'Stop' break } 'BeFalse' { $newExtentText = Convert-ShouldBeFalse -CommandAst $commandAst @convertParameters -ErrorAction 'Stop' break } 'BeNullOrEmpty' { $newExtentText = Convert-ShouldBeNullOrEmpty -CommandAst $commandAst @convertParameters -ErrorAction 'Stop' break } 'BeOfType' { $newExtentText = Convert-ShouldBeOfType -CommandAst $commandAst @convertParameters -ErrorAction 'Stop' break } 'Throw' { $isNegated = Test-PesterCommandNegated -CommandAst $commandAst if ($isNegated) { $newExtentText = Convert-ShouldNotThrow -CommandAst $commandAst @convertParameters -ErrorAction 'Stop' # Change start and end offsets to replace the entire commandAst.Parent.Extent.Text. $startOffset = $commandAst.Parent.Extent.StartOffset $endOffset = $commandAst.Parent.Extent.EndOffset } else { $newExtentText = Convert-ShouldThrow -CommandAst $commandAst @convertParameters -ErrorAction 'Stop' } break } 'Match' { $newExtentText = Convert-ShouldMatch -CommandAst $commandAst @convertParameters -ErrorAction 'Stop' break } 'MatchExactly' { $newExtentText = Convert-ShouldMatchExactly -CommandAst $commandAst @convertParameters -ErrorAction 'Stop' break } 'Contain' { $newExtentText = Convert-ShouldContain -CommandAst $commandAst @convertParameters -ErrorAction 'Stop' break } default { Write-Warning -Message ('Unsupported command operator ''{0}'' in extent: `{1}`' -f $operatorName, $commandAst.Extent.Text) $apply = $false } } } else { Write-Warning -Message ('Did not found any of the supported command operators in extent: `{0}`' -f $commandAst.Extent.Text) } if ($apply) { # Replace the portion of the script. $convertedScriptText = $convertedScriptText.Remove($startOffset, $endOffset - $startOffset).Insert($startOffset, $newExtentText) } } Write-Progress -Id 2 -ParentId 1 -Activity 'Converting Should command syntax' -Status 'Completed' -PercentComplete 100 -Completed } else { Write-Verbose -Message "No 'Should' command found in $filePath." } if ($PassThru) { $convertedScriptText } else { Set-Content -Path $filePath -Value $convertedScriptText -NoNewLine -ErrorAction 'Stop' } } } end { Write-Progress -Id 1 -Activity 'Converting Pester syntax' -Status 'Completed' -PercentComplete 100 -Completed } } #EndRegion '.\Public\Convert-PesterSyntax.ps1' 313 |