diff --git a/Plaster/Private/ConvertFrom-JsonManifest.ps1 b/Plaster/Private/ConvertFrom-JsonManifest.ps1 index f5dbf03..8610992 100644 --- a/Plaster/Private/ConvertFrom-JsonManifest.ps1 +++ b/Plaster/Private/ConvertFrom-JsonManifest.ps1 @@ -71,7 +71,7 @@ function ConvertFrom-JsonManifest { $paramElement.SetAttribute('prompt', $param.prompt) } - if ($param.default) { + if ($param.PSObject.Properties.Match('default').Count -gt 0) { if ($param.default -is [array]) { $paramElement.SetAttribute('default', ($param.default -join ',')) } else { diff --git a/Plaster/Private/Read-PromptForInput.ps1 b/Plaster/Private/Read-PromptForInput.ps1 index e4aa2bf..e5f44f3 100644 --- a/Plaster/Private/Read-PromptForInput.ps1 +++ b/Plaster/Private/Read-PromptForInput.ps1 @@ -3,7 +3,8 @@ function Read-PromptForInput { param( $prompt, $default, - $pattern + $pattern, + [switch]$AllowEmpty ) if (!$pattern) { $patternMatch = $true @@ -11,7 +12,7 @@ function Read-PromptForInput { do { $value = Read-Host -Prompt $prompt - if (!$value -and $default) { + if (!$value -and ($AllowEmpty -or $default)) { $value = $default $patternMatch = $true } elseif ($value -and $pattern) { @@ -21,7 +22,7 @@ function Read-PromptForInput { $PSCmdlet.WriteDebug("Value '$value' did not match the pattern '$pattern'") } } - } while (!$value -or !$patternMatch) + } while ((!$value -and !$AllowEmpty) -or !$patternMatch) $value } diff --git a/Plaster/Private/Resolve-ProcessParameter.ps1 b/Plaster/Private/Resolve-ProcessParameter.ps1 index 17acb9d..537e308 100644 --- a/Plaster/Private/Resolve-ProcessParameter.ps1 +++ b/Plaster/Private/Resolve-ProcessParameter.ps1 @@ -76,7 +76,7 @@ function Resolve-ProcessParameter { } } # Prompt the user for text input. - $value = Read-PromptForInput $prompt $default @splat + $value = Read-PromptForInput $prompt $default $pattern -AllowEmpty:$Node.HasAttribute('default') $valueToStore = $value } 'user-fullname' { diff --git a/tests/EmptyDefaultTextParameter.Tests.ps1 b/tests/EmptyDefaultTextParameter.Tests.ps1 new file mode 100644 index 0000000..03ee639 --- /dev/null +++ b/tests/EmptyDefaultTextParameter.Tests.ps1 @@ -0,0 +1,106 @@ +BeforeDiscovery { + if ($null -eq $env:BHProjectPath) { + $path = Join-Path -Path $PSScriptRoot -ChildPath '..\build.ps1' + . $path -Task Build + } + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path $outputModVerDir "$($env:BHProjectName).psd1" + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} + +Describe 'Empty text parameter defaults' -Tag 'Unit' { + InModuleScope $env:BHProjectName { + It 'preserves an explicit empty JSON default' { + $manifest = ConvertFrom-JsonManifest -JsonContent @' +{ + "schemaVersion": "2.0", + "metadata": { + "name": "EmptyDefault", + "id": "513d2fdc-3cce-47d9-9531-d85114efb224", + "version": "1.0.0", + "title": "Empty default", + "description": "Tests explicit empty defaults.", + "author": "Plaster", + "tags": ["Test"] + }, + "parameters": [ + { + "name": "ModuleDesc", + "type": "text", + "prompt": "Enter a description", + "default": "" + } + ], + "content": [ + { + "type": "file", + "source": "source.txt", + "destination": "source.txt" + } + ] +} +'@ + + $parameter = $manifest.plasterManifest.parameters.parameter + $parameter.HasAttribute('default') | Should -BeTrue + $parameter.default | Should -BeExactly '' + } + + It 'accepts blank input when an empty default is explicit' { + $script:responses = [System.Collections.Generic.Queue[string]]::new() + $script:responses.Enqueue('') + $script:responses.Enqueue('unexpected second prompt') + Mock Read-Host { $script:responses.Dequeue() } + + $result = Read-PromptForInput -prompt 'Enter a description' -default '' -AllowEmpty + + $result | Should -BeExactly '' + Should -Invoke Read-Host -Times 1 -Exactly + } + It 'completes a JSON template when its text default is empty' { + $templatePath = Join-Path $TestDrive 'template' + $destinationPath = Join-Path $TestDrive 'output' + New-Item -ItemType Directory -Path $templatePath | Out-Null + @' +{ + "schemaVersion": "2.0", + "metadata": { + "name": "EmptyDefault", + "id": "513d2fdc-3cce-47d9-9531-d85114efb224", + "version": "1.0.0", + "title": "Empty default", + "description": "Tests explicit empty defaults.", + "author": "Plaster", + "tags": ["Test"] + }, + "parameters": [ + { + "name": "ModuleDesc", + "type": "text", + "prompt": "Enter a description", + "default": "" + } + ], + "content": [ + { + "type": "templateFile", + "source": "description.txt", + "destination": "description.txt" + } + ] +} +'@ | Set-Content -LiteralPath (Join-Path $templatePath 'plasterManifest.json') -Encoding utf8 + '<%=$PLASTER_PARAM_ModuleDesc%>' | Set-Content -LiteralPath (Join-Path $templatePath 'description.txt') -Encoding utf8 + Mock Read-Host { '' } + + Invoke-Plaster -TemplatePath $templatePath -DestinationPath $destinationPath -NoLogo + + (Get-Content -LiteralPath (Join-Path $destinationPath 'description.txt') -Raw).Trim() | Should -BeExactly '' + Should -Invoke Read-Host -Times 1 -Exactly + } + } +}