From de62fd2320302f0beef5d4502620d192b2e34043 Mon Sep 17 00:00:00 2001 From: Cory Knox Date: Thu, 13 Oct 2022 15:18:29 -0700 Subject: [PATCH] (#2591) Add headers to limit output commands When a user asks for limited output from Chocolatey, it is not uncommon to pipe that output to `ConvertFrom-String` or `ConvertFrom-Csv` and manually add headers to get back an object. This allows for getting a header row back so that the end user doesn't need to add their own headers and discern what they are. This also adds a StringResources static class that allows us to store constant strings in and use them across the code to reduce duplication. --- src/chocolatey/StringResources.cs | 20 +++ src/chocolatey/chocolatey.csproj | 1 + .../ApplicationParameters.cs | 1 + .../builders/ConfigurationBuilder.cs | 3 +- .../commands/ChocolateyApiKeyCommand.cs | 8 ++ .../commands/ChocolateyConfigCommand.cs | 3 + .../commands/ChocolateyFeatureCommand.cs | 3 + .../commands/ChocolateyInfoCommand.cs | 3 + .../commands/ChocolateyListCommand.cs | 3 + .../commands/ChocolateyOutdatedCommand.cs | 3 + .../commands/ChocolateyPinCommand.cs | 7 + .../commands/ChocolateySourceCommand.cs | 3 + .../commands/ChocolateyTemplateCommand.cs | 6 +- .../configuration/ChocolateyConfiguration.cs | 2 + .../ChocolateyConfigSettingsService.cs | 65 ++++++--- .../services/ChocolateyPackageService.cs | 24 +++- .../services/TemplateService.cs | 9 +- .../features/Headers.Tests.ps1 | 133 ++++++++++++++++++ 18 files changed, 271 insertions(+), 26 deletions(-) create mode 100644 src/chocolatey/StringResources.cs create mode 100644 tests/chocolatey-tests/features/Headers.Tests.ps1 diff --git a/src/chocolatey/StringResources.cs b/src/chocolatey/StringResources.cs new file mode 100644 index 0000000000..5570000a83 --- /dev/null +++ b/src/chocolatey/StringResources.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace chocolatey +{ + public static class StringResources + { + public static class OptionDescriptions + { + public const string DISPLAY_HEADERS = "Display headers - Display headers when limit-output is used. Requires "; + } + + public static class Options + { + public const string DISPLAY_HEADERS = "display-headers|use-headers"; + } + } +} diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index c59d36b1e9..9d2fedb0b1 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -360,6 +360,7 @@ + diff --git a/src/chocolatey/infrastructure.app/ApplicationParameters.cs b/src/chocolatey/infrastructure.app/ApplicationParameters.cs index 0b215850db..3ede1ecd00 100644 --- a/src/chocolatey/infrastructure.app/ApplicationParameters.cs +++ b/src/chocolatey/infrastructure.app/ApplicationParameters.cs @@ -208,6 +208,7 @@ public static class Features public static readonly string LogValidationResultsOnWarnings = "logValidationResultsOnWarnings"; public static readonly string UsePackageRepositoryOptimizations = "usePackageRepositoryOptimizations"; public static readonly string DisableCompatibilityChecks = "disableCompatibilityChecks"; + public static readonly string AlwaysDisplayHeaders = "alwaysDisplayHeaders"; } public static class Messages diff --git a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs index 39877f5c57..d62a567935 100644 --- a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs +++ b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs @@ -318,7 +318,8 @@ private static void set_feature_flags(ChocolateyConfiguration config, ConfigFile config.Features.LogValidationResultsOnWarnings = set_feature_flag(ApplicationParameters.Features.LogValidationResultsOnWarnings, configFileSettings, defaultEnabled: true, description: "Log validation results on warnings - Should the validation results be logged if there are warnings? Available in 0.10.12+."); config.Features.UsePackageRepositoryOptimizations = set_feature_flag(ApplicationParameters.Features.UsePackageRepositoryOptimizations, configFileSettings, defaultEnabled: true, description: "Use Package Repository Optimizations - Turn on optimizations for reducing bandwidth with repository queries during package install/upgrade/outdated operations. Should generally be left enabled, unless a repository needs to support older methods of query. When disabled, this makes queries similar to the way they were done in Chocolatey v0.10.11 and before. Available in 0.10.14+."); config.PromptForConfirmation = !set_feature_flag(ApplicationParameters.Features.AllowGlobalConfirmation, configFileSettings, defaultEnabled: false, description: "Prompt for confirmation in scripts or bypass."); - config.DisableCompatibilityChecks = set_feature_flag(ApplicationParameters.Features.DisableCompatibilityChecks, configFileSettings, defaultEnabled: false, description: "Disable Compatibility Checks - Should a warning we shown, before and after command execution, when a runtime compatibility check determines that there is an incompatibility between Chocolatey and Chocolatey Licensed Extension. Available in 1.1.0+"); + config.DisableCompatibilityChecks = set_feature_flag(ApplicationParameters.Features.DisableCompatibilityChecks, configFileSettings, defaultEnabled: false, description: "Disable Compatibility Checks - Should a warning be shown, before and after command execution, when a runtime compatibility check determines that there is an incompatibility between Chocolatey and Chocolatey Licensed Extension. Available in 1.1.0+"); + config.DisplayHeaders = set_feature_flag(ApplicationParameters.Features.AlwaysDisplayHeaders, configFileSettings, defaultEnabled: false, description: StringResources.OptionDescriptions.DISPLAY_HEADERS); } private static bool set_feature_flag(string featureName, ConfigFileSettings configFileSettings, bool defaultEnabled, string description) diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyApiKeyCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyApiKeyCommand.cs index 85742f60ee..309d59e19b 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyApiKeyCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyApiKeyCommand.cs @@ -51,6 +51,9 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon .Add("rem|remove", "Removes an API key from Chocolatey", option => configuration.ApiKeyCommand.Remove = true) + .Add(StringResources.Options.DISPLAY_HEADERS, + StringResources.OptionDescriptions.DISPLAY_HEADERS, + option => configuration.DisplayHeaders = true) ; } @@ -156,6 +159,11 @@ public virtual void run(ChocolateyConfiguration configuration) } else if (string.IsNullOrWhiteSpace(configuration.ApiKeyCommand.Key)) { + if (!configuration.RegularOutput && configuration.DisplayHeaders) + { + this.Log().Info("Source|Key"); + } + _configSettingsService.get_api_key(configuration, (key) => { string authenticatedString = string.IsNullOrWhiteSpace(key.Key) ? string.Empty : "(Authenticated)"; diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyConfigCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyConfigCommand.cs index 20e86760c3..e25fee88fb 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyConfigCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyConfigCommand.cs @@ -51,6 +51,9 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon "value=", "Value - the value of the config setting. Required with some actions. Defaults to empty.", option => configuration.ConfigCommand.ConfigValue = option.remove_surrounding_quotes()) + .Add(StringResources.Options.DISPLAY_HEADERS, + StringResources.OptionDescriptions.DISPLAY_HEADERS, + option => configuration.DisplayHeaders = true) ; } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyFeatureCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyFeatureCommand.cs index d568b5cffb..35ab03a5e7 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyFeatureCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyFeatureCommand.cs @@ -47,6 +47,9 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon .Add("n=|name=", "Name - the name of the source. Required with actions other than list. Defaults to empty.", option => configuration.FeatureCommand.Name = option.remove_surrounding_quotes()) + .Add(StringResources.Options.DISPLAY_HEADERS, + StringResources.OptionDescriptions.DISPLAY_HEADERS, + option => configuration.DisplayHeaders = true) ; } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs index de65dc3c0b..bb0e597504 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyInfoCommand.cs @@ -73,6 +73,9 @@ public override void configure_argument_parser(OptionSet optionSet, ChocolateyCo configuration.Features.UsePackageRepositoryOptimizations = false; } }) + .Add(StringResources.Options.DISPLAY_HEADERS, + StringResources.OptionDescriptions.DISPLAY_HEADERS, + option => configuration.DisplayHeaders = true) ; } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs index 5eaf697342..fbbebc5e48 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyListCommand.cs @@ -136,6 +136,9 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon configuration.Features.UsePackageRepositoryOptimizations = false; } }) + .Add(StringResources.Options.DISPLAY_HEADERS, + StringResources.OptionDescriptions.DISPLAY_HEADERS, + option => configuration.DisplayHeaders = true) ; } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs index 750fef4185..87682cb2d2 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyOutdatedCommand.cs @@ -71,6 +71,9 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon configuration.Features.UsePackageRepositoryOptimizations = false; } }) + .Add(StringResources.Options.DISPLAY_HEADERS, + StringResources.OptionDescriptions.DISPLAY_HEADERS, + option => configuration.DisplayHeaders = true) ; } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs index 53851701a0..c02deedb15 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyPinCommand.cs @@ -54,6 +54,9 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon .Add("version=", "Version - Used when multiple versions of a package are installed. Defaults to empty.", option => configuration.Version = option.remove_surrounding_quotes()) + .Add(StringResources.Options.DISPLAY_HEADERS, + StringResources.OptionDescriptions.DISPLAY_HEADERS, + option => configuration.DisplayHeaders = true) ; } @@ -172,6 +175,10 @@ public virtual void list_pins(IPackageManager packageManager, ChocolateyConfigur config.QuietOutput = quiet; config.Input = input; + if (!config.RegularOutput && config.DisplayHeaders) + { + this.Log().Info("PackageId|Version"); + } foreach (var pkg in packages.or_empty_list_if_null()) { var pkgInfo = _packageInfoService.get_package_information(pkg.Package); diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs index a3e0b817a0..b0e15a4958 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateySourceCommand.cs @@ -74,6 +74,9 @@ public virtual void configure_argument_parser(OptionSet optionSet, ChocolateyCon .Add("adminonly|admin-only", "Visible to Administrators Only - Should this source be visible to non-administrators? Requires business edition (v1.12.2+). Defaults to false. Available in 0.10.8+.", option => configuration.SourceCommand.VisibleToAdminsOnly = option != null) + .Add(StringResources.Options.DISPLAY_HEADERS, + StringResources.OptionDescriptions.DISPLAY_HEADERS, + option => configuration.DisplayHeaders = true) ; } diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyTemplateCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyTemplateCommand.cs index 0aa3db98a0..67b072f7e8 100644 --- a/src/chocolatey/infrastructure.app/commands/ChocolateyTemplateCommand.cs +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyTemplateCommand.cs @@ -44,7 +44,11 @@ public void configure_argument_parser(OptionSet optionSet, ChocolateyConfigurati optionSet .Add("n=|name=", "The name of the template to get information about.", - option => configuration.TemplateCommand.Name = option.remove_surrounding_quotes().ToLower()); + option => configuration.TemplateCommand.Name = option.remove_surrounding_quotes().ToLower()) + .Add(StringResources.Options.DISPLAY_HEADERS, + StringResources.OptionDescriptions.DISPLAY_HEADERS, + option => configuration.DisplayHeaders = true) + ; // todo: #2570 Allow for templates from an external path? Requires #1477 } diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs index 549ac89cb8..14c58b95be 100644 --- a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs @@ -56,6 +56,7 @@ public ChocolateyConfiguration() Proxy = new ProxyConfiguration(); ExportCommand = new ExportCommandConfiguration(); TemplateCommand = new TemplateCommandConfiguration(); + DisplayHeaders = false; #if DEBUG AllowUnofficialBuild = true; #endif @@ -328,6 +329,7 @@ private void append_output(StringBuilder propertyValues, string append) public string DownloadChecksumType { get; set; } public string DownloadChecksumType64 { get; set; } public bool PinPackage { get; set; } + public bool DisplayHeaders { get; set; } /// /// Configuration values provided by choco. diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs index 1058e7aff5..3249f24254 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyConfigSettingsService.cs @@ -61,6 +61,11 @@ public virtual bool skip_source(ConfigFileSourceSetting source, ChocolateyConfig public virtual IEnumerable source_list(ChocolateyConfiguration configuration) { + if (!configuration.RegularOutput && configuration.DisplayHeaders) + { + this.Log().Info("SourceId|Location|Disabled|UserName|Certificate|Priority|BypassProxy|AllowSelfService|AdminOnly"); + } + var list = new List(); foreach (var source in configFileSettings.Sources) { @@ -217,6 +222,11 @@ public void source_enable(ChocolateyConfiguration configuration) public void feature_list(ChocolateyConfiguration configuration) { + if (!configuration.RegularOutput && configuration.DisplayHeaders) + { + this.Log().Info("FeatureName|Enabled|Description"); + } + foreach (var feature in configFileSettings.Features) { if (configuration.RegularOutput) @@ -364,27 +374,42 @@ public void remove_api_key(ChocolateyConfiguration configuration) public void config_list(ChocolateyConfiguration configuration) { - this.Log().Info(ChocolateyLoggers.Important, "Settings"); - foreach (var config in configFileSettings.ConfigSettings) - { - this.Log().Info(() => "{0} = {1} | {2}".format_with(config.Key, config.Value, config.Description)); - } - - this.Log().Info(""); - this.Log().Info(ChocolateyLoggers.Important, "Sources"); - source_list(configuration); - this.Log().Info(""); - this.Log().Info(@"NOTE: Use choco source to interact with sources."); - this.Log().Info(""); - this.Log().Info(ChocolateyLoggers.Important, "Features"); - feature_list(configuration); - this.Log().Info(""); - this.Log().Info(@"NOTE: Use choco feature to interact with features."); - ; - this.Log().Info(""); - this.Log().Info(ChocolateyLoggers.Important, "API Keys"); - this.Log().Info(@"NOTE: Api Keys are not shown through this command. + if (configuration.RegularOutput) + { + this.Log().Info(ChocolateyLoggers.Important, "Settings"); + foreach (var config in configFileSettings.ConfigSettings) + { + this.Log().Info(() => "{0} = {1} | {2}".format_with(config.Key, config.Value, config.Description)); + } + + this.Log().Info(""); + this.Log().Info(ChocolateyLoggers.Important, "Sources"); + source_list(configuration); + this.Log().Info(""); + this.Log().Info(@"NOTE: Use choco source to interact with sources."); + this.Log().Info(""); + this.Log().Info(ChocolateyLoggers.Important, "Features"); + feature_list(configuration); + this.Log().Info(""); + this.Log().Info(@"NOTE: Use choco feature to interact with features."); + ; + this.Log().Info(""); + this.Log().Info(ChocolateyLoggers.Important, "API Keys"); + this.Log().Info(@"NOTE: Api Keys are not shown through this command. Use choco apikey to interact with API keys."); + } + else + { + if (configuration.DisplayHeaders) + { + this.Log().Info("Name|Value|Description"); + } + + foreach (var config in configFileSettings.ConfigSettings) + { + this.Log().Info(() => "{0}|{1}|{2}".format_with(config.Key, config.Value, config.Description)); + } + } } public void config_get(ChocolateyConfiguration configuration) diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index 5933f324ab..e845f49e06 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -174,7 +174,17 @@ public virtual IEnumerable list_run(ChocolateyConfiguration confi yield break; } - if (config.RegularOutput) this.Log().Debug(() => "Searching for package information"); + if (config.RegularOutput) + { + this.Log().Debug(() => "Searching for package information"); + } + else + { + if (config.DisplayHeaders) + { + this.Log().Info("PackageID|Version"); + } + } var packages = new List(); @@ -643,9 +653,19 @@ public virtual void outdated_run(ChocolateyConfiguration config) return; } - if (config.RegularOutput) this.Log().Info(ChocolateyLoggers.Important, @"Outdated Packages + if (config.RegularOutput) + { + this.Log().Info(ChocolateyLoggers.Important, @"Outdated Packages Output is package name | current version | available version | pinned? "); + } + else + { + if(config.DisplayHeaders) + { + this.Log().Info("PackageName|CurrentVersion|AvailableVersion|Pinned"); + } + } config.PackageNames = ApplicationParameters.AllPackages; config.UpgradeCommand.NotifyOnlyAvailableUpgrades = true; diff --git a/src/chocolatey/infrastructure.app/services/TemplateService.cs b/src/chocolatey/infrastructure.app/services/TemplateService.cs index 9e6d2355d2..48845596b5 100644 --- a/src/chocolatey/infrastructure.app/services/TemplateService.cs +++ b/src/chocolatey/infrastructure.app/services/TemplateService.cs @@ -232,6 +232,11 @@ public void list(ChocolateyConfiguration configuration) if (string.IsNullOrWhiteSpace(configuration.TemplateCommand.Name)) { + if (!configuration.RegularOutput && configuration.DisplayHeaders) + { + this.Log().Info("TemplateName|Version"); + } + if (templateDirList.Any()) { foreach (var templateDir in templateDirList) @@ -240,11 +245,11 @@ public void list(ChocolateyConfiguration configuration) list_custom_template_info(configuration, packageManager); } - this.Log().Info(configuration.RegularOutput ? "{0} Custom templates found at {1}{2}".format_with(templateDirList.Count(), ApplicationParameters.TemplatesLocation, Environment.NewLine) : string.Empty); + this.Log().Info(configuration.RegularOutput ? ChocolateyLoggers.Normal : ChocolateyLoggers.LogFileOnly, "{0} Custom templates found at {1}{2}".format_with(templateDirList.Count(), ApplicationParameters.TemplatesLocation, Environment.NewLine)); } else { - this.Log().Info(configuration.RegularOutput ? "No custom templates installed in {0}{1}".format_with(ApplicationParameters.TemplatesLocation, Environment.NewLine) : string.Empty); + this.Log().Info(configuration.RegularOutput ? ChocolateyLoggers.Normal : ChocolateyLoggers.LogFileOnly, "No custom templates installed in {0}{1}".format_with(ApplicationParameters.TemplatesLocation, Environment.NewLine)); } list_built_in_template_info(configuration, isBuiltInTemplateOverriden, isBuiltInOrDefaultTemplateDefault); diff --git a/tests/chocolatey-tests/features/Headers.Tests.ps1 b/tests/chocolatey-tests/features/Headers.Tests.ps1 new file mode 100644 index 0000000000..f2ff49bf79 --- /dev/null +++ b/tests/chocolatey-tests/features/Headers.Tests.ps1 @@ -0,0 +1,133 @@ +Import-Module helpers/common-helpers + +Describe "choco headers tests" -Tag Chocolatey, HeadersFeature { + BeforeDiscovery { + $Commands = @( + @{ + Command = 'list' + CommandLine = '--local-only' + ExpectedHeaders = 'PackageID|Version' + } + @{ + # + Command = 'info' + CommandLine = 'chocolatey --local-only' + ExpectedHeaders = 'PackageID|Version' + } + @{ + # Needs to pin something... + Command = 'pin' + ExpectedHeaders = 'PackageID|Version' + } + @{ + Command = 'outdated' + ExpectedHeaders = 'PackageName|CurrentVersion|AvailableVersion|Pinned' + } + @{ + Command = 'source' + ExpectedHeaders = 'SourceId|Location|Disabled|UserName|Certificate|Priority|BypassProxy|AllowSelfService|AdminOnly' + } + @{ + Command = 'config' + ExpectedHeaders = 'Name|Value|Description' + } + @{ + Command = 'feature' + ExpectedHeaders = 'FeatureName|Enabled|Description' + } + @{ + Command = 'apikey' + ExpectedHeaders = 'Source|Key' + } + @{ + Command = 'template' + ExpectedHeaders = 'TemplateName|Version' + } + ) + } + BeforeAll { + Initialize-ChocolateyTestInstall + + New-ChocolateyInstallSnapshot + } + + AfterAll { + Remove-ChocolateyTestInstall + } + + Context "Outputs headers for when '--display-headers' provided configured" -ForEach $Commands { + BeforeAll { + $Output = Invoke-Choco $Command $CommandLine --limit-output --display-headers + } + + It 'Exits success (0)' { + $Output.ExitCode | Should -Be 0 -Because $Output.String + } + + It "Displays appropriate header" { + # Some commands won't output anything but the header. In that case we want just the lines instead of indexing into it. + $ActualOutput = if ($Output.Lines.Count -gt 1) { + $Output.Lines[0] + } + else { + $Output.Lines + } + + $ActualOutput | Should -Be $ExpectedHeaders + } + + } + + Context "Does not output headers for by default" -ForEach $Commands { + BeforeAll { + $Output = Invoke-Choco $Command $CommandLine --limit-output + } + + It 'Exits success (0)' { + $Output.ExitCode | Should -Be 0 -Because $Output.String + } + + It "Does not display header" { + # Some commands won't output anything but the header. In that case we want just the lines instead of indexing into it. + $ActualOutput = if ($Output.Lines.Count -gt 1) { + $Output.Lines[0] + } + else { + $Output.Lines + } + + $ActualOutput | Should -Not -Be $ExpectedHeaders + } + + } + + Context "Outputs headers when feature enabled" { + BeforeAll { + Restore-ChocolateyInstallSnapshot + + Enable-ChocolateyFeature -Name AlwaysDisplayHeaders + } + + Context "Outputs headers for " -ForEach $Commands { + BeforeAll { + $Output = Invoke-Choco $Command $CommandLine --limit-output + } + + It 'Exits success (0)' { + $Output.ExitCode | Should -Be 0 -Because $Output.String + } + + It "Displays appropriate header" { + # Some commands won't output anything but the header. In that case we want just the lines instead of indexing into it. + $ActualOutput = if ($Output.Lines.Count -gt 1) { + $Output.Lines[0] + } + else { + $Output.Lines + } + + $ActualOutput | Should -Be $ExpectedHeaders + } + } + } +}