diff --git a/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs index 723e3c6690..ffab9d6203 100644 --- a/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs +++ b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs @@ -2,7 +2,6 @@ using System.CommandLine.Invocation; using FluentAssertions; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Xunit; diff --git a/src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs b/src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs index ad15258299..e7652ef9fe 100644 --- a/src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs +++ b/src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs @@ -5,569 +5,565 @@ using System.CommandLine.Help; using System.IO; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using FluentAssertions; -using FluentAssertions.Common; using Xunit; using static System.Environment; -namespace System.CommandLine.Tests.Help +namespace System.CommandLine.Tests.Help; + +public partial class HelpBuilderTests { - public partial class HelpBuilderTests + public class Customization { - public class Customization - { - private readonly HelpBuilder _helpBuilder; - private readonly StringWriter _console; - private readonly string _columnPadding; - private readonly string _indentation; + private readonly HelpBuilder _helpBuilder; + private readonly StringWriter _console; + private readonly string _columnPadding; + private readonly string _indentation; - public Customization() - { - _console = new(); - _helpBuilder = GetHelpBuilder(LargeMaxWidth); - _columnPadding = new string(' ', ColumnGutterWidth); - _indentation = new string(' ', IndentationWidth); - } + public Customization() + { + _console = new(); + _helpBuilder = GetHelpBuilder(LargeMaxWidth); + _columnPadding = new string(' ', ColumnGutterWidth); + _indentation = new string(' ', IndentationWidth); + } - private HelpBuilder GetHelpBuilder(int maxWidth) => new (maxWidth); + private HelpBuilder GetHelpBuilder(int maxWidth) => new (maxWidth); - [Fact] - public void Option_can_customize_displayed_default_value() + [Fact] + public void Option_can_customize_displayed_default_value() + { + var option = new CliOption("--the-option") { DefaultValueFactory = _ => "not 42" }; + var command = new CliCommand("the-command", "command help") { - var option = new CliOption("--the-option") { DefaultValueFactory = _ => "not 42" }; - var command = new CliCommand("the-command", "command help") - { - option - }; + option + }; - _helpBuilder.CustomizeSymbol(option, defaultValue: "42"); + _helpBuilder.CustomizeSymbol(option, defaultValue: "42"); - _helpBuilder.Write(command, _console); - var expected = - $"Options:{NewLine}" + - $"{_indentation}--the-option{_columnPadding}[default: 42]{NewLine}{NewLine}"; + _helpBuilder.Write(command, _console); + var expected = + $"Options:{NewLine}" + + $"{_indentation}--the-option{_columnPadding}[default: 42]{NewLine}{NewLine}"; - _console.ToString().Should().Contain(expected); - } + _console.ToString().Should().Contain(expected); + } - [Fact] - public void Option_can_customize_first_column_text() + [Fact] + public void Option_can_customize_first_column_text() + { + var option = new CliOption("--the-option") { Description = "option description" }; + var command = new CliCommand("the-command", "command help") { - var option = new CliOption("--the-option") { Description = "option description" }; - var command = new CliCommand("the-command", "command help") - { - option - }; + option + }; - _helpBuilder.CustomizeSymbol(option, firstColumnText: "other-name"); + _helpBuilder.CustomizeSymbol(option, firstColumnText: "other-name"); - _helpBuilder.Write(command, _console); - var expected = - $"Options:{NewLine}" + - $"{_indentation}other-name{_columnPadding}option description{NewLine}{NewLine}"; + _helpBuilder.Write(command, _console); + var expected = + $"Options:{NewLine}" + + $"{_indentation}other-name{_columnPadding}option description{NewLine}{NewLine}"; - _console.ToString().Should().Contain(expected); - } + _console.ToString().Should().Contain(expected); + } - [Fact] - public void Option_can_customize_first_column_text_based_on_parse_result() + [Fact] + public void Option_can_customize_first_column_text_based_on_parse_result() + { + var option = new CliOption("option"); + var commandA = new CliCommand("a", "a command help") { - var option = new CliOption("option"); - var commandA = new CliCommand("a", "a command help") - { - option - }; - var commandB = new CliCommand("b", "b command help") - { - option - }; - var command = new CliCommand("root", "root command help") - { - commandA, commandB - }; - var optionAFirstColumnText = "option a help"; - var optionBFirstColumnText = "option b help"; - - var helpBuilder = new HelpBuilder(LargeMaxWidth); - helpBuilder.CustomizeSymbol(option, firstColumnText: ctx => - ctx.Command.Equals(commandA) - ? optionAFirstColumnText - : optionBFirstColumnText); - command.Options.Add(new HelpOption() + option + }; + var commandB = new CliCommand("b", "b command help") + { + option + }; + var command = new CliCommand("root", "root command help") + { + commandA, commandB + }; + var optionAFirstColumnText = "option a help"; + var optionBFirstColumnText = "option b help"; + + var helpBuilder = new HelpBuilder(LargeMaxWidth); + helpBuilder.CustomizeSymbol(option, firstColumnText: ctx => + ctx.Command.Equals(commandA) + ? optionAFirstColumnText + : optionBFirstColumnText); + command.Options.Add(new HelpOption() + { + Action = new HelpAction() { - Action = new HelpAction() - { - Builder = helpBuilder - } - }); + Builder = helpBuilder + } + }); - var console = new StringWriter(); - var config = new CliConfiguration(command) - { - Output = console - }; - command.Parse("root a -h", config).Invoke(); - console.ToString().Should().Contain(optionAFirstColumnText); - - console = new StringWriter(); - config.Output = console; - command.Parse("root b -h", config).Invoke(); - console.ToString().Should().Contain(optionBFirstColumnText); - } + var console = new StringWriter(); + var config = new CliConfiguration(command) + { + Output = console + }; + command.Parse("root a -h", config).Invoke(); + console.ToString().Should().Contain(optionAFirstColumnText); + + console = new StringWriter(); + config.Output = console; + command.Parse("root b -h", config).Invoke(); + console.ToString().Should().Contain(optionBFirstColumnText); + } - [Fact] - public void Option_can_customize_second_column_text_based_on_parse_result() + [Fact] + public void Option_can_customize_second_column_text_based_on_parse_result() + { + var option = new CliOption("option"); + var commandA = new CliCommand("a", "a command help") { - var option = new CliOption("option"); - var commandA = new CliCommand("a", "a command help") - { - option - }; - var commandB = new CliCommand("b", "b command help") - { - option - }; - var command = new CliCommand("root", "root command help") - { - commandA, commandB - }; - var optionADescription = "option a help"; - var optionBDescription = "option b help"; - - var helpBuilder = new HelpBuilder(LargeMaxWidth); - helpBuilder.CustomizeSymbol(option, secondColumnText: ctx => - ctx.Command.Equals(commandA) - ? optionADescription - : optionBDescription); - command.Options.Add(new HelpOption - { - Action = new HelpAction - { - Builder = helpBuilder - } - }); + option + }; + var commandB = new CliCommand("b", "b command help") + { + option + }; + var command = new CliCommand("root", "root command help") + { + commandA, commandB + }; + var optionADescription = "option a help"; + var optionBDescription = "option b help"; + + var helpBuilder = new HelpBuilder(LargeMaxWidth); + helpBuilder.CustomizeSymbol(option, secondColumnText: ctx => + ctx.Command.Equals(commandA) + ? optionADescription + : optionBDescription); + command.Options.Add(new HelpOption + { + Action = new HelpAction + { + Builder = helpBuilder + } + }); - var config = new CliConfiguration(command) - { - Output = new StringWriter() - }; + var config = new CliConfiguration(command) + { + Output = new StringWriter() + }; - config.Invoke("root a -h"); - config.Output.ToString().Should().Contain($"option {optionADescription}"); + config.Invoke("root a -h"); + config.Output.ToString().Should().Contain($"option {optionADescription}"); - config.Output = new StringWriter(); - config.Invoke("root b -h"); - config.Output.ToString().Should().Contain($"option {optionBDescription}"); - } + config.Output = new StringWriter(); + config.Invoke("root b -h"); + config.Output.ToString().Should().Contain($"option {optionBDescription}"); + } - [Fact] - public void Subcommand_can_customize_first_column_text() + [Fact] + public void Subcommand_can_customize_first_column_text() + { + var subcommand = new CliCommand("subcommand", "subcommand description"); + var command = new CliCommand("the-command", "command help") { - var subcommand = new CliCommand("subcommand", "subcommand description"); - var command = new CliCommand("the-command", "command help") - { - subcommand - }; + subcommand + }; - _helpBuilder.CustomizeSymbol(subcommand, firstColumnText: "other-name"); + _helpBuilder.CustomizeSymbol(subcommand, firstColumnText: "other-name"); - _helpBuilder.Write(command, _console); - var expected = - $"Commands:{NewLine}" + - $"{_indentation}other-name{_columnPadding}subcommand description{NewLine}{NewLine}"; + _helpBuilder.Write(command, _console); + var expected = + $"Commands:{NewLine}" + + $"{_indentation}other-name{_columnPadding}subcommand description{NewLine}{NewLine}"; - _console.ToString().Should().Contain(expected); - } + _console.ToString().Should().Contain(expected); + } - [Fact] - public void Command_arguments_can_customize_first_column_text() + [Fact] + public void Command_arguments_can_customize_first_column_text() + { + var argument = new CliArgument("arg-name") { Description = "arg description" }; + var command = new CliCommand("the-command", "command help") { - var argument = new CliArgument("arg-name") { Description = "arg description" }; - var command = new CliCommand("the-command", "command help") - { - argument - }; + argument + }; - _helpBuilder.CustomizeSymbol(argument, firstColumnText: ""); + _helpBuilder.CustomizeSymbol(argument, firstColumnText: ""); - _helpBuilder.Write(command, _console); - var expected = - $"Arguments:{NewLine}" + - $"{_indentation}{_columnPadding}arg description{NewLine}{NewLine}"; + _helpBuilder.Write(command, _console); + var expected = + $"Arguments:{NewLine}" + + $"{_indentation}{_columnPadding}arg description{NewLine}{NewLine}"; - _console.ToString().Should().Contain(expected); - } + _console.ToString().Should().Contain(expected); + } - [Fact] - public void Command_arguments_can_customize_second_column_text() + [Fact] + public void Command_arguments_can_customize_second_column_text() + { + var argument = new CliArgument("some-arg") { - var argument = new CliArgument("some-arg") - { - Description = "Default description", - DefaultValueFactory = _ => "not 42" - }; - var command = new CliCommand("the-command", "command help") - { - argument - }; + Description = "Default description", + DefaultValueFactory = _ => "not 42" + }; + var command = new CliCommand("the-command", "command help") + { + argument + }; - _helpBuilder.CustomizeSymbol(argument, secondColumnText: "Custom description"); + _helpBuilder.CustomizeSymbol(argument, secondColumnText: "Custom description"); - _helpBuilder.Write(command, _console); - var expected = - $"Arguments:{NewLine}" + - $"{_indentation}{_columnPadding}Custom description [default: not 42]{NewLine}{NewLine}"; + _helpBuilder.Write(command, _console); + var expected = + $"Arguments:{NewLine}" + + $"{_indentation}{_columnPadding}Custom description [default: not 42]{NewLine}{NewLine}"; - _console.ToString().Should().Contain(expected); - } + _console.ToString().Should().Contain(expected); + } - [Fact] - public void Command_arguments_can_customize_default_value() + [Fact] + public void Command_arguments_can_customize_default_value() + { + var argument = new CliArgument("some-arg") { - var argument = new CliArgument("some-arg") - { - DefaultValueFactory = (_) => "not 42" - }; - var command = new CliCommand("the-command", "command help") - { - argument - }; + DefaultValueFactory = (_) => "not 42" + }; + var command = new CliCommand("the-command", "command help") + { + argument + }; - _helpBuilder.CustomizeSymbol(argument, defaultValue: "42"); + _helpBuilder.CustomizeSymbol(argument, defaultValue: "42"); - _helpBuilder.Write(command, _console); - var expected = - $"Arguments:{NewLine}" + - $"{_indentation}{_columnPadding}[default: 42]{NewLine}{NewLine}"; + _helpBuilder.Write(command, _console); + var expected = + $"Arguments:{NewLine}" + + $"{_indentation}{_columnPadding}[default: 42]{NewLine}{NewLine}"; - _console.ToString().Should().Contain(expected); - } + _console.ToString().Should().Contain(expected); + } - [Fact] - public void Customize_throws_when_symbol_is_null() - { - Action action = () => new HelpBuilder().CustomizeSymbol(null!, ""); - action.Should().Throw(); - } + [Fact] + public void Customize_throws_when_symbol_is_null() + { + Action action = () => new HelpBuilder().CustomizeSymbol(null!, ""); + action.Should().Throw(); + } - [Theory] - [InlineData(false, false, "--option \\s*description")] - [InlineData(true, false, "custom 1st\\s*description")] - [InlineData(false, true, "--option \\s*custom 2nd")] - [InlineData(true, true, "custom 1st\\s*custom 2nd")] - public void Option_can_fallback_to_default_when_customizing(bool conditionA, bool conditionB, string expected) - { - var command = new CliCommand("test"); - var option = new CliOption("--option") { Description = "description" }; + [Theory] + [InlineData(false, false, "--option \\s*description")] + [InlineData(true, false, "custom 1st\\s*description")] + [InlineData(false, true, "--option \\s*custom 2nd")] + [InlineData(true, true, "custom 1st\\s*custom 2nd")] + public void Option_can_fallback_to_default_when_customizing(bool conditionA, bool conditionB, string expected) + { + var command = new CliCommand("test"); + var option = new CliOption("--option") { Description = "description" }; - command.Options.Add(option); + command.Options.Add(option); - var helpBuilder = new HelpBuilder(LargeMaxWidth); - helpBuilder.CustomizeSymbol(option, - firstColumnText: ctx => conditionA ? "custom 1st" : HelpBuilder.Default.GetOptionUsageLabel(option), - secondColumnText: ctx => conditionB ? "custom 2nd" : option.Description ?? string.Empty); + var helpBuilder = new HelpBuilder(LargeMaxWidth); + helpBuilder.CustomizeSymbol(option, + firstColumnText: ctx => conditionA ? "custom 1st" : HelpBuilder.Default.GetOptionUsageLabel(option), + secondColumnText: ctx => conditionB ? "custom 2nd" : option.Description ?? string.Empty); - command.Options.Add(new HelpOption() + command.Options.Add(new HelpOption + { + Action = new HelpAction { - Action = new HelpAction() - { - Builder = helpBuilder - } - }); + Builder = helpBuilder + } + }); - CliConfiguration config = new (command); - var console = new StringWriter(); - config.Output = console; - command.Parse("test -h", config).Invoke(); - console.ToString().Should().MatchRegex(expected); - } + CliConfiguration config = new (command); + var console = new StringWriter(); + config.Output = console; + command.Parse("test -h", config).Invoke(); + console.ToString().Should().MatchRegex(expected); + } - [Theory] - [InlineData(false, false, false, "\\\\s*description\\s*\\[default\\: default\\]")] - [InlineData(true, false, false, "custom 1st\\s*description\\s*\\[default\\: default\\]")] - [InlineData(false, true, false, "\\\\s*custom 2nd\\s*\\[default\\: default\\]")] - [InlineData(true, true, false, "custom 1st\\s*custom 2nd\\s*\\[default\\: default\\]")] - [InlineData(false, false, true, "\\\\s*description\\s*\\[default\\: custom def\\]")] - [InlineData(true, false, true, "custom 1st\\s*description\\s*\\[default\\: custom def\\]")] - [InlineData(false, true, true, "\\\\s*custom 2nd\\s*\\[default\\: custom def\\]")] - [InlineData(true, true, true, "custom 1st\\s*custom 2nd\\s*\\[default\\: custom def\\]")] - public void Argument_can_fallback_to_default_when_customizing( - bool conditionA, - bool conditionB, - bool conditionC, - string expected) - { - var command = new CliCommand("test"); - var argument = new CliArgument("arg") - { - Description = "description", - DefaultValueFactory = _ => "default" - }; + [Theory] + [InlineData(false, false, false, "\\\\s*description\\s*\\[default\\: default\\]")] + [InlineData(true, false, false, "custom 1st\\s*description\\s*\\[default\\: default\\]")] + [InlineData(false, true, false, "\\\\s*custom 2nd\\s*\\[default\\: default\\]")] + [InlineData(true, true, false, "custom 1st\\s*custom 2nd\\s*\\[default\\: default\\]")] + [InlineData(false, false, true, "\\\\s*description\\s*\\[default\\: custom def\\]")] + [InlineData(true, false, true, "custom 1st\\s*description\\s*\\[default\\: custom def\\]")] + [InlineData(false, true, true, "\\\\s*custom 2nd\\s*\\[default\\: custom def\\]")] + [InlineData(true, true, true, "custom 1st\\s*custom 2nd\\s*\\[default\\: custom def\\]")] + public void Argument_can_fallback_to_default_when_customizing( + bool conditionA, + bool conditionB, + bool conditionC, + string expected) + { + var command = new CliCommand("test"); + var argument = new CliArgument("arg") + { + Description = "description", + DefaultValueFactory = _ => "default" + }; - command.Arguments.Add(argument); + command.Arguments.Add(argument); - var helpBuilder = new HelpBuilder(LargeMaxWidth); - helpBuilder.CustomizeSymbol(argument, - firstColumnText: ctx => conditionA ? "custom 1st" : HelpBuilder.Default.GetArgumentUsageLabel(argument), - secondColumnText: ctx => conditionB ? "custom 2nd" : HelpBuilder.Default.GetArgumentDescription(argument), - defaultValue: ctx => conditionC ? "custom def" : HelpBuilder.Default.GetArgumentDefaultValue(argument)); + var helpBuilder = new HelpBuilder(LargeMaxWidth); + helpBuilder.CustomizeSymbol(argument, + firstColumnText: ctx => conditionA ? "custom 1st" : HelpBuilder.Default.GetArgumentUsageLabel(argument), + secondColumnText: ctx => conditionB ? "custom 2nd" : HelpBuilder.Default.GetArgumentDescription(argument), + defaultValue: ctx => conditionC ? "custom def" : HelpBuilder.Default.GetArgumentDefaultValue(argument)); - CliConfiguration config = new (command); + CliConfiguration config = new (command); - command.Options.Add(new HelpOption + command.Options.Add(new HelpOption + { + Action = new HelpAction { - Action = new HelpAction - { - Builder = helpBuilder - } - }); + Builder = helpBuilder + } + }); - config.Output = new StringWriter(); - command.Parse("test -h", config).Invoke(); - config.Output.ToString().Should().MatchRegex(expected); - } + config.Output = new StringWriter(); + command.Parse("test -h", config).Invoke(); + config.Output.ToString().Should().MatchRegex(expected); + } - [Fact] - public void Individual_symbols_can_be_customized() - { - var subcommand = new CliCommand("subcommand", "The default command description"); - var option = new CliOption("-x") { Description = "The default option description" }; - var argument = new CliArgument("int-value") { Description = "The default argument description" }; + [Fact] + public void Individual_symbols_can_be_customized() + { + var subcommand = new CliCommand("subcommand", "The default command description"); + var option = new CliOption("-x") { Description = "The default option description" }; + var argument = new CliArgument("int-value") { Description = "The default argument description" }; - var rootCommand = new CliRootCommand - { - subcommand, - option, - argument, - }; + var rootCommand = new CliRootCommand + { + subcommand, + option, + argument, + }; - CliConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; + CliConfiguration config = new(rootCommand) + { + Output = new StringWriter() + }; - ParseResult parseResult = rootCommand.Parse("-h", config); + ParseResult parseResult = rootCommand.Parse("-h", config); - if (parseResult.Action is HelpAction helpAction) - { - helpAction.Builder.CustomizeSymbol(subcommand, secondColumnText: "The custom command description"); - helpAction.Builder.CustomizeSymbol(option, secondColumnText: "The custom option description"); - helpAction.Builder.CustomizeSymbol(argument, secondColumnText: "The custom argument description"); - } + if (parseResult.Action is HelpAction helpAction) + { + helpAction.Builder.CustomizeSymbol(subcommand, secondColumnText: "The custom command description"); + helpAction.Builder.CustomizeSymbol(option, secondColumnText: "The custom option description"); + helpAction.Builder.CustomizeSymbol(argument, secondColumnText: "The custom argument description"); + } - parseResult.Invoke(); + parseResult.Invoke(); - config.Output - .ToString() - .Should() - .ContainAll("The custom command description", - "The custom option description", - "The custom argument description"); - } + config.Output + .ToString() + .Should() + .ContainAll("The custom command description", + "The custom option description", + "The custom argument description"); + } - [Fact] - public void Help_sections_can_be_replaced() + [Fact] + public void Help_sections_can_be_replaced() + { + CliConfiguration config = new(new CliRootCommand()) { - CliConfiguration config = new(new CliRootCommand()) - { - Output = new StringWriter() - }; + Output = new StringWriter() + }; - ParseResult parseResult = config.Parse("-h"); + ParseResult parseResult = config.Parse("-h"); - if (parseResult.Action is HelpAction helpAction) - { - helpAction.Builder.CustomizeLayout(CustomLayout); - } + if (parseResult.Action is HelpAction helpAction) + { + helpAction.Builder.CustomizeLayout(CustomLayout); + } - parseResult.Invoke(); + parseResult.Invoke(); - config.Output.ToString().Should().Be($"one{NewLine}{NewLine}two{NewLine}{NewLine}three{NewLine}{NewLine}"); + config.Output.ToString().Should().Be($"one{NewLine}{NewLine}two{NewLine}{NewLine}three{NewLine}{NewLine}"); - IEnumerable> CustomLayout(HelpContext _) - { - yield return ctx => { ctx.Output.WriteLine("one"); return true; }; - yield return ctx => { ctx.Output.WriteLine("two"); return true; }; - yield return ctx => { ctx.Output.WriteLine("three"); return true; }; - } + IEnumerable> CustomLayout(HelpContext _) + { + yield return ctx => { ctx.Output.WriteLine("one"); return true; }; + yield return ctx => { ctx.Output.WriteLine("two"); return true; }; + yield return ctx => { ctx.Output.WriteLine("three"); return true; }; } + } - [Fact] - public void Help_sections_can_be_supplemented() + [Fact] + public void Help_sections_can_be_supplemented() + { + CliConfiguration config = new(new CliRootCommand("hello")) { - CliConfiguration config = new(new CliRootCommand("hello")) - { - Output = new StringWriter(), - }; - - var defaultHelp = GetDefaultHelp(config.RootCommand); - - ParseResult parseResult = config.Parse("-h"); - - if (parseResult.Action is HelpAction helpAction) - { - helpAction.Builder.CustomizeLayout(CustomLayout); - } + Output = new StringWriter(), + }; - parseResult.Invoke(); + var defaultHelp = GetDefaultHelp(config.RootCommand); - var output = config.Output.ToString(); + ParseResult parseResult = config.Parse("-h"); - var expected = $"first{NewLine}{NewLine}{defaultHelp}{NewLine}last{NewLine}{NewLine}"; + if (parseResult.Action is HelpAction helpAction) + { + helpAction.Builder.CustomizeLayout(CustomLayout); + } - output.Should().Be(expected); + parseResult.Invoke(); - IEnumerable> CustomLayout(HelpContext _) - { - yield return ctx => { ctx.Output.WriteLine("first"); return true; }; + var output = config.Output.ToString(); - foreach (var section in HelpBuilder.Default.GetLayout()) - { - yield return section; - } + var expected = $"first{NewLine}{NewLine}{defaultHelp}{NewLine}last{NewLine}{NewLine}"; - yield return ctx => { ctx.Output.WriteLine("last"); return true; }; - } - } + output.Should().Be(expected); - [Fact] - public void Layout_can_be_composed_dynamically_based_on_context() + IEnumerable> CustomLayout(HelpContext _) { - HelpBuilder helpBuilder = new(); - var commandWithTypicalHelp = new CliCommand("typical"); - var commandWithCustomHelp = new CliCommand("custom"); - var command = new CliRootCommand - { - commandWithTypicalHelp, - commandWithCustomHelp - }; + yield return ctx => { ctx.Output.WriteLine("first"); return true; }; - command.Options.OfType().Single().Action = new HelpAction + foreach (var section in HelpBuilder.Default.GetLayout()) { - Builder = helpBuilder - }; - - var config = new CliConfiguration(command); - helpBuilder.CustomizeLayout(c => - c.Command == commandWithTypicalHelp - ? HelpBuilder.Default.GetLayout() - : new Func[] { c => { c.Output.WriteLine("Custom layout!"); return true; } } - .Concat(HelpBuilder.Default.GetLayout())); + yield return section; + } - var typicalOutput = new StringWriter(); - config.Output = typicalOutput; - command.Parse("typical -h", config).Invoke(); + yield return ctx => { ctx.Output.WriteLine("last"); return true; }; + } + } - var customOutput = new StringWriter(); - config.Output = customOutput; - command.Parse("custom -h", config).Invoke(); + [Fact] + public void Layout_can_be_composed_dynamically_based_on_context() + { + HelpBuilder helpBuilder = new(); + var commandWithTypicalHelp = new CliCommand("typical"); + var commandWithCustomHelp = new CliCommand("custom"); + var command = new CliRootCommand + { + commandWithTypicalHelp, + commandWithCustomHelp + }; - typicalOutput.ToString().Should().Be(GetDefaultHelp(commandWithTypicalHelp, false)); - customOutput.ToString().Should().Be($"Custom layout!{NewLine}{NewLine}{GetDefaultHelp(commandWithCustomHelp, false)}"); - } + command.Options.OfType().Single().Action = new HelpAction + { + Builder = helpBuilder + }; + + var config = new CliConfiguration(command); + helpBuilder.CustomizeLayout(c => + c.Command == commandWithTypicalHelp + ? HelpBuilder.Default.GetLayout() + : new Func[] { c => { c.Output.WriteLine("Custom layout!"); return true; } } + .Concat(HelpBuilder.Default.GetLayout())); + + var typicalOutput = new StringWriter(); + config.Output = typicalOutput; + command.Parse("typical -h", config).Invoke(); + + var customOutput = new StringWriter(); + config.Output = customOutput; + command.Parse("custom -h", config).Invoke(); + + typicalOutput.ToString().Should().Be(GetDefaultHelp(commandWithTypicalHelp, false)); + customOutput.ToString().Should().Be($"Custom layout!{NewLine}{NewLine}{GetDefaultHelp(commandWithCustomHelp, false)}"); + } - [Fact] - public void Help_default_sections_can_be_wrapped() + [Fact] + public void Help_default_sections_can_be_wrapped() + { + CliCommand command = new("test") { - CliCommand command = new("test") + new CliOption("--option") { - new CliOption("--option") - { - Description = "option description", - HelpName = "option" - }, - new HelpOption + Description = "option description", + HelpName = "option" + }, + new HelpOption + { + Action = new HelpAction { - Action = new HelpAction - { - Builder = new HelpBuilder(30) - } + Builder = new HelpBuilder(30) } - }; + } + }; - CliConfiguration config = new(command) - { - Output = new StringWriter() - }; - - config.Invoke("test -h"); - - string result = config.Output.ToString(); - result.Should().Be( - $"Description:{NewLine}{NewLine}" + - $"Usage:{NewLine} test [options]{NewLine}{NewLine}" + - $"Options:{NewLine}" + - $" --option option {NewLine}" + - $"