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