diff --git a/src/Main.ps1 b/src/Main.ps1 index 8bd8e4426..f35f190af 100644 --- a/src/Main.ps1 +++ b/src/Main.ps1 @@ -702,6 +702,8 @@ function Invoke-Pester { & $SafeCommands['Get-Variable'] 'Configuration' -Scope Local | Remove-Variable + Resolve-AutoEnabledConfiguration -PesterPreference $PesterPreference + # $sessionState = Set-SessionStateHint -PassThru -Hint "Caller - Captured in Invoke-Pester" -SessionState $PSCmdlet.SessionState $sessionState = $PSCmdlet.SessionState @@ -1092,6 +1094,13 @@ function Convert-PesterLegacyParameterSet ($BoundParameters) { return $Configuration } +function Resolve-AutoEnabledConfiguration { + param ([PesterConfiguration] $PesterPreference) + + $PesterPreference.CodeCoverage.ResolveEnabled() + $PesterPreference.TestResult.ResolveEnabled() +} + function ConvertTo-Pester4Result { <# diff --git a/src/csharp/Pester/ConfigurationSection.cs b/src/csharp/Pester/ConfigurationSection.cs index 27e0bbba7..cf6a297ed 100644 --- a/src/csharp/Pester/ConfigurationSection.cs +++ b/src/csharp/Pester/ConfigurationSection.cs @@ -15,6 +15,8 @@ // to have in "type accelerator" form, but without the hassle of actually adding it as a type accelerator // that way you can easily do `[PesterConfiguration]::Default` and then inspect it, or cast a hashtable to it +using System.Reflection; + namespace Pester { public abstract class ConfigurationSection @@ -29,5 +31,36 @@ public override string ToString() { return _description; } + + /// + /// If this section has an Enabled option that was not explicitly modified, + /// and any other option in the section was modified, auto-enable the section. + /// + public void ResolveEnabled() + { + var enabledProperty = GetType().GetProperty("Enabled", BindingFlags.Public | BindingFlags.Instance); + if (enabledProperty == null || enabledProperty.PropertyType != typeof(BoolOption)) + return; + + var enabled = (BoolOption)enabledProperty.GetValue(this); + if (enabled == null || enabled.IsModified) + return; + + foreach (var property in GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + if (property.Name == "Enabled") + continue; + + if (!typeof(Option).IsAssignableFrom(property.PropertyType)) + continue; + + var option = (Option)property.GetValue(this); + if (option != null && option.IsModified) + { + enabledProperty.SetValue(this, (BoolOption)true); + return; + } + } + } } } diff --git a/tst/Pester.RSpec.Configuration.ts.ps1 b/tst/Pester.RSpec.Configuration.ts.ps1 index b37945939..dee8a5d6d 100644 --- a/tst/Pester.RSpec.Configuration.ts.ps1 +++ b/tst/Pester.RSpec.Configuration.ts.ps1 @@ -583,6 +583,150 @@ i -PassThru:$PassThru { } } + b "auto-enabling output features" { + t "CodeCoverage.Path auto-enables code coverage" { + $coverageScript = "$PSScriptRoot/CoverageTestFile.ps1" + $coverageOutputPath = [IO.Path]::GetTempFileName() + Remove-Item $coverageOutputPath -Force + + try { + $r = Invoke-Pester -Configuration ([PesterConfiguration]@{ + Run = @{ + ScriptBlock = { + Describe "auto coverage" { + It "covers script" { + . $coverageScript + } + } + }.GetNewClosure() + PassThru = $true + } + Output = @{ + Verbosity = 'None' + } + CodeCoverage = @{ + Path = $coverageScript + OutputPath = $coverageOutputPath + } + }) + + $r.CodeCoverage | Verify-NotNull + (Test-Path $coverageOutputPath) | Verify-True + } + finally { + if (Test-Path $coverageOutputPath) { + Remove-Item $coverageOutputPath -Force -ErrorAction Ignore + } + } + } + + t "CodeCoverage.Enabled = `$false still disables code coverage" { + $coverageScript = "$PSScriptRoot/CoverageTestFile.ps1" + $coverageOutputPath = [IO.Path]::GetTempFileName() + Remove-Item $coverageOutputPath -Force + + try { + $r = Invoke-Pester -Configuration ([PesterConfiguration]@{ + Run = @{ + ScriptBlock = { + Describe "disabled coverage" { + It "does not export coverage" { + . $coverageScript + } + } + }.GetNewClosure() + PassThru = $true + } + Output = @{ + Verbosity = 'None' + } + CodeCoverage = @{ + Enabled = $false + Path = $coverageScript + OutputPath = $coverageOutputPath + } + }) + + $r.CodeCoverage | Verify-Null + (Test-Path $coverageOutputPath) | Verify-False + } + finally { + if (Test-Path $coverageOutputPath) { + Remove-Item $coverageOutputPath -Force -ErrorAction Ignore + } + } + } + + t "TestResult.OutputPath auto-enables test result export" { + $testResultOutputPath = [IO.Path]::GetTempFileName() + Remove-Item $testResultOutputPath -Force + + try { + $r = Invoke-Pester -Configuration ([PesterConfiguration]@{ + Run = @{ + ScriptBlock = { + Describe "auto testresult" { + It "exports xml" { + $true | Should -Be $true + } + } + } + PassThru = $true + } + Output = @{ + Verbosity = 'None' + } + TestResult = @{ + OutputPath = $testResultOutputPath + } + }) + + $r.Result | Verify-Equal 'Passed' + (Test-Path $testResultOutputPath) | Verify-True + } + finally { + if (Test-Path $testResultOutputPath) { + Remove-Item $testResultOutputPath -Force -ErrorAction Ignore + } + } + } + + t "TestResult.Enabled = `$false still disables test result export" { + $testResultOutputPath = [IO.Path]::GetTempFileName() + Remove-Item $testResultOutputPath -Force + + try { + $r = Invoke-Pester -Configuration ([PesterConfiguration]@{ + Run = @{ + ScriptBlock = { + Describe "disabled testresult" { + It "does not export xml" { + $true | Should -Be $true + } + } + } + PassThru = $true + } + Output = @{ + Verbosity = 'None' + } + TestResult = @{ + Enabled = $false + OutputPath = $testResultOutputPath + } + }) + + $r.Result | Verify-Equal 'Passed' + (Test-Path $testResultOutputPath) | Verify-False + } + finally { + if (Test-Path $testResultOutputPath) { + Remove-Item $testResultOutputPath -Force -ErrorAction Ignore + } + } + } + } + b "configuration modified at runtime" { t "changes at runtime doesn't leak to advanced configuration object" { $c = [PesterConfiguration] @{