Skip to content

Commit

Permalink
Add support for BeIn (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
johlju authored Jul 14, 2024
1 parent 8cba13b commit 9ad45fc
Show file tree
Hide file tree
Showing 15 changed files with 754 additions and 271 deletions.
Binary file removed .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ output/
markdownissues.txt
node_modules
package-lock.json
.DS_Store
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- BeFalse
- BeGreaterOrEqual
- BeGreaterThan
- BeIn
- BeLessOrEqual
- BeLessThan
- BeLike
Expand Down
246 changes: 246 additions & 0 deletions source/Private/Convert-ShouldBeIn.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
<#
.SYNOPSIS
Converts a command `Should -BeIn` to the specified Pester syntax.
.DESCRIPTION
The Convert-ShouldContain function is used to convert a command `Should -BeIn` 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 -BeIn @("Test", "Test2")')
Convert-ShouldContain -CommandAst $commandAst -Pester6
This example converts the `Should -BeIn @("Test", "Test2")` command to Pester 6 syntax.
.NOTES
Pester 5 Syntax:
Should -BeIn [[-ActualValue] <Object>] [[-ExpectedValue] <Object>] [[-Because] <string>] [-Not]
Positional parameters:
Position 1: ExpectedValue
Position 2: Because
Position 3: ActualValue
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-ShouldBeIn
{
[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 ($script:localizedData.Convert_Should_Debug_ParsingCommandAst -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 ($script:localizedData.Convert_Should_Debug_ConvertingFromTo -f $sourceSyntaxVersion, '6')

# Add the correct Pester command based on negation
if ($isNegated)
{
$newExtentText = 'Should-NotContainCollection'
}
else
{
$newExtentText = 'Should-ContainCollection'
}

$getPesterCommandParameterParameters = @{
CommandAst = $CommandAst
CommandName = 'Should'
IgnoreParameter = @(
'BeIn'
'Not'
)
PositionalParameter = @(
'ExpectedValue'
'Because'
'ActualValue'
)
NamedParameter = @()
}

$commandParameters = Get-PesterCommandParameter @getPesterCommandParameterParameters

$pipelineExtentText = Get-PipelineBeforeShould -CommandAst $CommandAst -ParsePipeline

# NOTE! This switches the arguments between expected and actual values.
$originalActualValue = $commandParameters.ActualValue
$commandParameters.ActualValue = $commandParameters.ExpectedValue
$commandParameters.ExpectedValue = $originalActualValue

# If we do not end up with a value for ExpectValue we need to get it from the pipeline.
$isPipeline = $null -eq $commandParameters.ExpectedValue

if ($isPipeline)
{
<#
ActualValue was not part of the arguments as either positional or
named parameter, assume there is a pipeline and the original
ActualValue should be converted to Expected. Also using the
position and positional property values from original ActualValue
for the new ExpectedValue.
#>
$commandParameters.ExpectedValue = @{
Position = $commandParameters.ActualValue.Position
Positional = $commandParameters.ActualValue.Positional
ExtentText = '({0})' -f $pipelineExtentText
}

<#
We must put the new actual value on the pipeline before the Should
command, so we need to make sure it is not added as positional.
#>
$commandParameters.ActualValue.Position = 0
$commandParameters.ActualValue.Positional = $false
}

# 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 the actual value was originally passed in the pipeline, we should do the same.
if ($commandParameters.ActualValue -and -not $isPipeline)
{
$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'
{
if ($isPipeline)
{
continue
}

$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
}
}

if ($isPipeline)
{
$newExtentText = '{0} | {1}' -f $commandParameters.ActualValue.ExtentText, $newExtentText
}

Write-Debug -Message ($script:localizedData.Convert_Should_Debug_ConvertedCommand -f $CommandAst.Extent.Text, $newExtentText)

return $newExtentText
}
2 changes: 1 addition & 1 deletion source/Private/Convert-ShouldNotThrow.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ function Convert-ShouldNotThrow
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)
$newExtentText = '$null = & ({0})' -f (Get-PipelineBeforeShould -CommandAst $CommandAst -ParameterName 'ActualValue' -ParsePipeline)
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion source/Private/Get-PesterCommandParameter.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ function Get-PesterCommandParameter
$positionalParameterHashtable.$parameter = @{
Position = $positionalCounter
Positional = $true
ExtentText = $commandElement[$elementCounter].Extent.Text #? $commandElement.Extent.Text : $null
ExtentText = $commandElement[$elementCounter].Extent.Text
}

# Increment the positional counter.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Retrieves the script block associated with a pipeline or parameter.
.DESCRIPTION
The Get-ShouldThrowScriptBlock function is used to retrieve the script block
The Get-PipelineBeforeShould 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.
Expand All @@ -23,12 +23,12 @@
The function returns the text of the script block if found, otherwise it returns $null.
.EXAMPLE
Get-ShouldThrowScriptBlock -CommandAst $commandAst -ParameterName 'ActualValue' -ParsePipeline
Get-PipelineBeforeShould -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
function Get-PipelineBeforeShould
{
[CmdletBinding()]
[OutputType([System.String])]
Expand Down
40 changes: 40 additions & 0 deletions source/Private/Test-IsPipelinePart.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

<#
.SYNOPSIS
Determines if a command is part of a pipeline.
.DESCRIPTION
The Test-IsPipelinePart function checks if a given command is part of a
pipeline. It examines the parent of the CommandAst and determines if it
is a PipelineAst.
.PARAMETER CommandAst
The CommandAst object representing the command to be checked.
.EXAMPLE
Test-IsPipelinePart -CommandAst $commandAst
Determines if the specified command is part of a pipeline.
.OUTPUTS
System.Boolean
Returns $true if the command is part of a pipeline, otherwise returns $false.
#>
function Test-IsPipelinePart
{
[CmdletBinding()]
[OutputType([System.Boolean])]
param
(
[Parameter(Mandatory = $true)]
[System.Management.Automation.Language.CommandAst]
$CommandAst
)

# Check if the parent of the CommandAst is a PipelineAst
$isPartOfPipeline = $CommandAst.Parent -and $CommandAst.Parent -is [System.Management.Automation.Language.PipelineAst] -and $CommandAst.Parent.PipelineElements.Count -gt 1

# Return true if part of a pipeline, false otherwise
return $isPartOfPipeline
}
14 changes: 14 additions & 0 deletions source/Public/Convert-PesterSyntax.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,20 @@ function Convert-PesterSyntax
break
}

'BeIn'
{
$newExtentText = Convert-ShouldBeIn -CommandAst $commandAst @convertParameters -ErrorAction 'Stop'

if ((Test-IsPipelinePart -CommandAst $commandAst -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
}

break
}

default
{
Write-Warning -Message ($script:localizedData.Convert_PesterSyntax_Warning_UnsupportedCommandOperator -f $operatorName, $commandAst.Extent.Text)
Expand Down
Loading

0 comments on commit 9ad45fc

Please sign in to comment.