Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Describe "TabCompletion" -Tags CI {
BeforeAll {
$separator = [System.IO.Path]::DirectorySeparatorChar
$nullConditionalFeatureDisabled = -not $EnabledExperimentalFeatures.Contains('PSNullConditionalOperators')
}

It 'Should complete Command' {
Expand Down Expand Up @@ -33,12 +34,12 @@ Describe "TabCompletion" -Tags CI {
$res.CompletionMatches[0].CompletionText | Should -BeExactly 'ToString('
}

It 'Should complete dotnet method with null conditional operator' {
It 'Should complete dotnet method with null conditional operator' -skip:$nullConditionalFeatureDisabled {
$res = TabExpansion2 -inputScript '(1)?.ToSt' -cursorColumn '(1)?.ToSt'.Length
$res.CompletionMatches[0].CompletionText | Should -BeExactly 'ToString('
}

It 'Should complete dotnet method with null conditional operator without first letter' {
It 'Should complete dotnet method with null conditional operator without first letter' -skip:$nullConditionalFeatureDisabled {
$res = TabExpansion2 -inputScript '(1)?.' -cursorColumn '(1)?.'.Length
$res.CompletionMatches[0].CompletionText | Should -BeExactly 'CompareTo('
}
Expand Down
58 changes: 58 additions & 0 deletions test/powershell/Language/Operators/NullConditional.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,15 @@ Describe 'NullConditionalMemberAccess' -Tag 'CI' {
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
$PSDefaultParameterValues["it:skip"] = $true
}

function ExecuteTestIfFeatureIsEnabled([string] $TestContet)
{
if ($skipTest) {
Set-ItResult -Skipped -Because "PSNullConditionalOperators feature is disabled"
} else {
Invoke-Expression $testContent
}
}
}

AfterAll {
Expand All @@ -285,6 +294,8 @@ Describe 'NullConditionalMemberAccess' -Tag 'CI' {
}

It 'Can get member value of a non-null variable' {

$testContent = @'
${psObj}?.name | Should -BeExactly 'value'
${array}?.length | Should -Be 3
${hash}?.a | Should -Be 1
Expand All @@ -293,50 +304,81 @@ Describe 'NullConditionalMemberAccess' -Tag 'CI' {
(Get-Item $TestDrive)?.EnumerateFiles()?.Name | Should -BeExactly 'testfile.txt'

[int32]::MaxValue?.ToString() | Should -BeExactly '2147483647'
'@

ExecuteTestIfFeatureIsEnabled -TestContent $testContent
}

It 'Can get null when variable is null' {
$testContent = @'
${nonExistent}?.name | Should -BeNullOrEmpty
${nonExistent}?.MyMethod() | Should -BeNullOrEmpty

(get-process -Name doesnotexist -ErrorAction SilentlyContinue)?.Id | Should -BeNullOrEmpty
'@

ExecuteTestIfFeatureIsEnabled -TestContent $testContent
}

It 'Use ?. operator multiple times in statement' {
$testContent = @'
${psObj}?.name?.nonExistent | Should -BeNullOrEmpty
${psObj}?.nonExistent?.nonExistent | Should -BeNullOrEmpty
${nonExistent}?.nonExistent?.nonExistent | Should -BeNullOrEmpty

${psObj}?.nested?.name | Should -BeExactly 'valuenested'
${psObj}?.nestedMethod?.GetHello() | Should -BeExactly 'hello'
'@

ExecuteTestIfFeatureIsEnabled -TestContent $testContent
}

It 'Use ?. on a dynamic method name' {
$testContent = @'
$methodName = 'ToLongDateString'
(Get-Date '11/11/2019')?.$methodName() | Should -BeExactly 'Monday, November 11, 2019'

${doesNotExist}?.$methodName() | Should -BeNullOrEmpty
'@

ExecuteTestIfFeatureIsEnabled -TestContent $testContent
}

It 'Use ?. on a dynamic method name that does not exist' {
$testContent = @'
$methodName = 'DoesNotExist'
{ (Get-Date '11/11/2019')?.$methodName() } | Should -Throw -ErrorId 'MethodNotFound'
'@

ExecuteTestIfFeatureIsEnabled -TestContent $testContent
}

It 'Use ?. on a dynamic method name that does not exist' {
$testContent = @'
$methodName = $null
{ (Get-Date '11/11/2019')?.$methodName() } | Should -Throw -ErrorId 'MethodNotFound'
'@

ExecuteTestIfFeatureIsEnabled -TestContent $testContent
}

It 'Use ?. on a dynamic property name' {
$testContent = @'
$propName = 'Name'
(Get-Process -Id $pid)?.$propName | Should -BeLike 'pwsh*'

${doesNotExist}?.$propName() | Should -BeNullOrEmpty
'@

ExecuteTestIfFeatureIsEnabled -TestContent $testContent
}

It 'Should throw error when method does not exist' {
$testContent = @'
{ ${psObj}?.nestedMethod?.NonExistent() } | Should -Throw -ErrorId 'MethodNotFound'
'@

ExecuteTestIfFeatureIsEnabled -TestContent $testContent
}
}

Expand All @@ -352,30 +394,46 @@ Describe 'NullConditionalMemberAccess' -Tag 'CI' {
}

It 'Can index can call properties' {
$testContent = @'
${array}?[0] | Should -Be 1
${array}?[0,1] | Should -Be @(1,2)
${array}?[0..2] | Should -Be @(1,2,3)
${array}?[-2] | Should -Be 2

${hash}?['a'] | Should -Be 1
'@

ExecuteTestIfFeatureIsEnabled -TestContent $testContent
}

It 'Indexing in null items should be null' {
$testContent = @'
${doesnotExist}?[0] | Should -BeNullOrEmpty
${doesnotExist}?[0,1] | Should -BeNullOrEmpty
${doesnotExist}?[0..2] | Should -BeNullOrEmpty
${doesnotExist}?[-2] | Should -BeNullOrEmpty

${doesnotExist}?['a'] | Should -BeNullOrEmpty
'@

ExecuteTestIfFeatureIsEnabled -TestContent $testContent
}

It 'Can call methods on indexed items' {
$testContent = @'
${dateArray}?[0]?.ToLongDateString() | Should -BeExactly 'Friday, November 1, 2019'
'@

ExecuteTestIfFeatureIsEnabled -TestContent $testContent
}

It 'Calling a method on nonexistent item give null' {
$testContent = @'
${dateArray}?[1234]?.ToLongDateString() | Should -BeNullOrEmpty
${doesNotExist}?[0]?.MyGetMethod() | Should -BeNullOrEmpty
'@

ExecuteTestIfFeatureIsEnabled -TestContent $testContent
}
}
}
27 changes: 15 additions & 12 deletions test/powershell/Language/Parser/Parsing.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -295,18 +295,21 @@ Describe 'null conditional member access statement parsing' -Tag 'CI' {
}
}

ShouldBeParseError '[datetime]?::now' ExpectedValueExpression,UnexpectedToken 11,11
ShouldBeParseError '$x ?.name' ExpectedValueExpression,UnexpectedToken 4,4
ShouldBeParseError 'Get-Date ?.ToString()' ExpectedExpression 20
ShouldBeParseError '${x}?.' MissingPropertyName 6
ShouldBeParseError '${x}?.name = "value"' InvalidLeftHandSide 0

ShouldBeParseError '[datetime]?[0]' MissingTypename,ExpectedValueExpression,UnexpectedToken 12,11,11
ShouldBeParseError '${x} ?[1]' MissingTypename,ExpectedValueExpression,UnexpectedToken 7,6,6
ShouldBeParseError '${x}?[]' MissingArrayIndexExpression 6
ShouldBeParseError '${x}?[-]' MissingExpressionAfterOperator 7
ShouldBeParseError '${x}?[ ]' MissingArrayIndexExpression 6
ShouldBeParseError '${x}?[0] = 1' InvalidLeftHandSide 0
# We need to add this check as parsing on script block is done before an `It` is called.
if (-not $skipTest) {
ShouldBeParseError '[datetime]?::now' ExpectedValueExpression, UnexpectedToken 11, 11
ShouldBeParseError '$x ?.name' ExpectedValueExpression, UnexpectedToken 4, 4
ShouldBeParseError 'Get-Date ?.ToString()' ExpectedExpression 20
ShouldBeParseError '${x}?.' MissingPropertyName 6
ShouldBeParseError '${x}?.name = "value"' InvalidLeftHandSide 0

ShouldBeParseError '[datetime]?[0]' MissingTypename, ExpectedValueExpression, UnexpectedToken 12, 11, 11
ShouldBeParseError '${x} ?[1]' MissingTypename, ExpectedValueExpression, UnexpectedToken 7, 6, 6
ShouldBeParseError '${x}?[]' MissingArrayIndexExpression 6
ShouldBeParseError '${x}?[-]' MissingExpressionAfterOperator 7
ShouldBeParseError '${x}?[ ]' MissingArrayIndexExpression 6
ShouldBeParseError '${x}?[0] = 1' InvalidLeftHandSide 0
}
}

Describe 'splatting parsing' -Tags "CI" {
Expand Down
4 changes: 4 additions & 0 deletions test/tools/TestMetadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"ExperimentalFeatures": {
"ExpTest.FeatureOne": [ "test/powershell/engine/ExperimentalFeature/ExperimentalFeature.Basic.Tests.ps1" ],
"PSNullConditionalOperators": [
"test/powershell/Language/Operators/NullConditional.Tests.ps1",
"test/powershell/Language/Parser/Parsing.Tests.ps1",
"/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1" ],
"PSCultureInvariantReplaceOperator": [ "test/powershell/Language/Operators/ReplaceOperator.Tests.ps1" ]
}
}