Create Test, allow for conditional trim

This commit is contained in:
Benjamin Michaelis 2022-10-13 08:58:30 -07:00 committed by Patrik Svensson
parent a91a3c12ad
commit f6a7c96413
15 changed files with 398 additions and 317 deletions

View File

@ -71,6 +71,23 @@ public static class ConfiguratorExtensions
configurator.Settings.StrictParsing = true; configurator.Settings.StrictParsing = true;
return configurator; return configurator;
}
/// <summary>
/// Tells the help writer whether or not to trim trailing period.
/// </summary>
/// <param name="configurator">The configurator.</param>
/// <param name="trimTrailingPeriods">True to trim trailing period (default), false to not.</param>
/// <returns>A configurator that can be used to configure the application further.</returns>
public static IConfigurator TrimTrailingPeriods(this IConfigurator configurator, bool trimTrailingPeriods)
{
if (configurator == null)
{
throw new ArgumentNullException(nameof(configurator));
}
configurator.Settings.TrimTrailingPeriod = trimTrailingPeriods;
return configurator;
} }
/// <summary> /// <summary>

View File

@ -34,7 +34,12 @@ public interface ICommandAppSettings
/// <summary> /// <summary>
/// Gets or sets case sensitivity. /// Gets or sets case sensitivity.
/// </summary> /// </summary>
CaseSensitivity CaseSensitivity { get; set; } CaseSensitivity CaseSensitivity { get; set; }
/// <summary>
/// Gets or sets a value indicating whether trailing period of a description is trimmed.
/// </summary>
bool TrimTrailingPeriod { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether or not parsing is strict. /// Gets or sets a value indicating whether or not parsing is strict.

View File

@ -10,13 +10,14 @@ internal sealed class CommandAppSettings : ICommandAppSettings
public CaseSensitivity CaseSensitivity { get; set; } public CaseSensitivity CaseSensitivity { get; set; }
public bool PropagateExceptions { get; set; } public bool PropagateExceptions { get; set; }
public bool ValidateExamples { get; set; } public bool ValidateExamples { get; set; }
public bool TrimTrailingPeriod { get; set; } = true;
public bool StrictParsing { get; set; } public bool StrictParsing { get; set; }
public ParsingMode ParsingMode => public ParsingMode ParsingMode =>
StrictParsing ? ParsingMode.Strict : ParsingMode.Relaxed; StrictParsing ? ParsingMode.Strict : ParsingMode.Relaxed;
public Func<Exception, int>? ExceptionHandler { get; set; } public Func<Exception, int>? ExceptionHandler { get; set; }
public CommandAppSettings(ITypeRegistrar registrar) public CommandAppSettings(ITypeRegistrar registrar)
{ {
Registrar = new TypeRegistrar(registrar); Registrar = new TypeRegistrar(registrar);

View File

@ -373,11 +373,20 @@ internal static class HelpWriter
{ {
arguments.Style("silver", $"<{argument.Name.EscapeMarkup()}>"); arguments.Style("silver", $"<{argument.Name.EscapeMarkup()}>");
arguments.Space(); arguments.Space();
} }
grid.AddRow( if (model.TrimTrailingPeriod)
arguments.ToString().TrimEnd(), {
child.Description ?? " "); grid.AddRow(
arguments.ToString().TrimEnd(),
child.Description?.TrimEnd('.') ?? " ");
}
else
{
grid.AddRow(
arguments.ToString().TrimEnd(),
child.Description ?? " ");
}
} }
result.Add(grid); result.Add(grid);

View File

@ -6,7 +6,8 @@ internal sealed class CommandModel : ICommandContainer
public ParsingMode ParsingMode { get; } public ParsingMode ParsingMode { get; }
public CommandInfo? DefaultCommand { get; } public CommandInfo? DefaultCommand { get; }
public IList<CommandInfo> Commands { get; } public IList<CommandInfo> Commands { get; }
public IList<string[]> Examples { get; } public IList<string[]> Examples { get; }
public bool TrimTrailingPeriod { get; }
public CommandModel( public CommandModel(
CommandAppSettings settings, CommandAppSettings settings,
@ -16,9 +17,10 @@ internal sealed class CommandModel : ICommandContainer
{ {
ApplicationName = settings.ApplicationName; ApplicationName = settings.ApplicationName;
ParsingMode = settings.ParsingMode; ParsingMode = settings.ParsingMode;
TrimTrailingPeriod = settings.TrimTrailingPeriod;
DefaultCommand = defaultCommand; DefaultCommand = defaultCommand;
Commands = new List<CommandInfo>(commands ?? Array.Empty<CommandInfo>()); Commands = new List<CommandInfo>(commands ?? Array.Empty<CommandInfo>());
Examples = new List<string[]>(examples ?? Array.Empty<string[]>()); Examples = new List<string[]>(examples ?? Array.Empty<string[]>());
} }
public string GetApplicationName() public string GetApplicationName()

View File

@ -1,4 +1,4 @@
DESCRIPTION: DESCRIPTION:
Contains settings for a cat. Contains settings for a cat.
USAGE: USAGE:
@ -14,4 +14,4 @@ OPTIONS:
--agility <VALUE> The agility between 0 and 100 --agility <VALUE> The agility between 0 and 100
COMMANDS: COMMANDS:
lion <TEETH> The lion command. lion <TEETH> The lion command

View File

@ -1,4 +1,4 @@
DESCRIPTION: DESCRIPTION:
The animal command. The animal command.
USAGE: USAGE:
@ -15,5 +15,5 @@ OPTIONS:
-a, --alive Indicates whether or not the animal is alive -a, --alive Indicates whether or not the animal is alive
COMMANDS: COMMANDS:
dog <AGE> The dog command. dog <AGE> The dog command
horse The horse command. horse The horse command

View File

@ -0,0 +1,10 @@
USAGE:
myapp [OPTIONS] <COMMAND>
OPTIONS:
-h, --help Prints help information
-v, --version Prints version information
COMMANDS:
dog <AGE> The dog command.
horse The horse command.

View File

@ -6,5 +6,5 @@ OPTIONS:
-v, --version Prints version information -v, --version Prints version information
COMMANDS: COMMANDS:
dog <AGE> The dog command. dog <AGE> The dog command
horse The horse command. horse The horse command

View File

@ -6,6 +6,6 @@ OPTIONS:
-v, --version Prints version information -v, --version Prints version information
COMMANDS: COMMANDS:
dog <AGE> The dog command. dog <AGE> The dog command
horse The horse command. horse The horse command
giraffe <LENGTH> The giraffe command. giraffe <LENGTH> The giraffe command

View File

@ -10,5 +10,5 @@ OPTIONS:
-v, --version Prints version information -v, --version Prints version information
COMMANDS: COMMANDS:
dog <AGE> The dog command. dog <AGE> The dog command
horse The horse command. horse The horse command

View File

@ -10,5 +10,5 @@ OPTIONS:
-v, --version Prints version information -v, --version Prints version information
COMMANDS: COMMANDS:
dog <AGE> The dog command. dog <AGE> The dog command
horse The horse command. horse The horse command

View File

@ -10,4 +10,4 @@ OPTIONS:
-v, --version Prints version information -v, --version Prints version information
COMMANDS: COMMANDS:
animal The animal command. animal The animal command

View File

@ -31,4 +31,15 @@
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" /> <ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="Expectations\Help\Description_No_Trailing_Period.Output.received.txt">
<ParentFile>$([System.String]::Copy('%(FileName)').Split('.')[0])</ParentFile>
<DependentUpon>%(ParentFile).cs</DependentUpon>
</None>
<None Update="Expectations\Help\Description_No_Trailing_Period.Output.verified.txt">
<ParentFile>$([System.String]::Copy('%(FileName)').Split('.')[0])</ParentFile>
<DependentUpon>%(ParentFile).cs</DependentUpon>
</None>
</ItemGroup>
</Project> </Project>

View File

@ -1,293 +1,319 @@
namespace Spectre.Console.Tests.Unit.Cli; using Spectre.Console.Cli;
public sealed partial class CommandAppTests namespace Spectre.Console.Tests.Unit.Cli;
{
[UsesVerify] public sealed partial class CommandAppTests
[ExpectationPath("Help")] {
public class Help [UsesVerify]
{ [ExpectationPath("Help")]
[Fact] public class Help
[Expectation("Root")] {
public Task Should_Output_Root_Correctly() [Fact]
{ [Expectation("Root")]
// Given public Task Should_Output_Root_Correctly()
var fixture = new CommandAppTester(); {
fixture.Configure(configurator => // Given
{ var fixture = new CommandAppTester();
configurator.SetApplicationName("myapp"); fixture.Configure(configurator =>
configurator.AddCommand<DogCommand>("dog"); {
configurator.AddCommand<HorseCommand>("horse"); configurator.SetApplicationName("myapp");
configurator.AddCommand<GiraffeCommand>("giraffe"); configurator.AddCommand<DogCommand>("dog");
}); configurator.AddCommand<HorseCommand>("horse");
configurator.AddCommand<GiraffeCommand>("giraffe");
// When });
var result = fixture.Run("--help");
// When
// Then var result = fixture.Run("--help");
return Verifier.Verify(result.Output);
} // Then
return Verifier.Verify(result.Output);
[Fact] }
[Expectation("Hidden_Commands")]
public Task Should_Skip_Hidden_Commands() [Fact]
{ [Expectation("Hidden_Commands")]
// Given public Task Should_Skip_Hidden_Commands()
var fixture = new CommandAppTester(); {
fixture.Configure(configurator => // Given
{ var fixture = new CommandAppTester();
configurator.SetApplicationName("myapp"); fixture.Configure(configurator =>
configurator.AddCommand<DogCommand>("dog"); {
configurator.AddCommand<HorseCommand>("horse"); configurator.SetApplicationName("myapp");
configurator.AddCommand<GiraffeCommand>("giraffe") configurator.AddCommand<DogCommand>("dog");
.WithExample(new[] { "giraffe", "123" }) configurator.AddCommand<HorseCommand>("horse");
.IsHidden(); configurator.AddCommand<GiraffeCommand>("giraffe")
}); .WithExample(new[] { "giraffe", "123" })
.IsHidden();
// When });
var result = fixture.Run("--help");
// When
// Then var result = fixture.Run("--help");
return Verifier.Verify(result.Output);
} // Then
return Verifier.Verify(result.Output);
[Fact] }
[Expectation("Command")]
public Task Should_Output_Command_Correctly() [Fact]
{ [Expectation("Description_No_Trailing_Period")]
// Given public Task Should_Not_Trim_Description_Trailing_Period()
var fixture = new CommandAppTester(); {
fixture.Configure(configurator => // Given
{ var fixture = new CommandAppTester();
configurator.SetApplicationName("myapp"); fixture.Configure(configurator =>
configurator.AddBranch<CatSettings>("cat", animal => {
{ configurator.SetApplicationName("myapp");
animal.SetDescription("Contains settings for a cat."); configurator.AddCommand<DogCommand>("dog");
animal.AddCommand<LionCommand>("lion"); configurator.AddCommand<HorseCommand>("horse");
}); configurator.AddCommand<GiraffeCommand>("giraffe")
}); .WithExample(new[] { "giraffe", "123" })
.IsHidden();
// When configurator.TrimTrailingPeriods(false);
var result = fixture.Run("cat", "--help"); });
// Then // When
return Verifier.Verify(result.Output); var result = fixture.Run("--help");
}
// Then
[Fact] return Verifier.Verify(result.Output);
[Expectation("Leaf")] }
public Task Should_Output_Leaf_Correctly()
{ [Fact]
// Given [Expectation("Command")]
var fixture = new CommandAppTester(); public Task Should_Output_Command_Correctly()
fixture.Configure(configurator => {
{ // Given
configurator.SetApplicationName("myapp"); var fixture = new CommandAppTester();
configurator.AddBranch<CatSettings>("cat", animal => fixture.Configure(configurator =>
{ {
animal.SetDescription("Contains settings for a cat."); configurator.SetApplicationName("myapp");
animal.AddCommand<LionCommand>("lion"); configurator.AddBranch<CatSettings>("cat", animal =>
}); {
}); animal.SetDescription("Contains settings for a cat.");
animal.AddCommand<LionCommand>("lion");
// When });
var result = fixture.Run("cat", "lion", "--help"); });
// Then // When
return Verifier.Verify(result.Output); var result = fixture.Run("cat", "--help");
}
// Then
[Fact] return Verifier.Verify(result.Output);
[Expectation("Default")] }
public Task Should_Output_Default_Command_Correctly()
{ [Fact]
// Given [Expectation("Leaf")]
var fixture = new CommandAppTester(); public Task Should_Output_Leaf_Correctly()
fixture.SetDefaultCommand<LionCommand>(); {
fixture.Configure(configurator => // Given
{ var fixture = new CommandAppTester();
configurator.SetApplicationName("myapp"); fixture.Configure(configurator =>
}); {
configurator.SetApplicationName("myapp");
// When configurator.AddBranch<CatSettings>("cat", animal =>
var result = fixture.Run("--help"); {
animal.SetDescription("Contains settings for a cat.");
// Then animal.AddCommand<LionCommand>("lion");
return Verifier.Verify(result.Output); });
} });
[Fact] // When
[Expectation("RootExamples")] var result = fixture.Run("cat", "lion", "--help");
public Task Should_Output_Root_Examples_Defined_On_Root()
{ // Then
// Given return Verifier.Verify(result.Output);
var fixture = new CommandAppTester(); }
fixture.Configure(configurator =>
{ [Fact]
configurator.SetApplicationName("myapp"); [Expectation("Default")]
configurator.AddExample(new[] { "dog", "--name", "Rufus", "--age", "12", "--good-boy" }); public Task Should_Output_Default_Command_Correctly()
configurator.AddExample(new[] { "horse", "--name", "Brutus" }); {
configurator.AddCommand<DogCommand>("dog"); // Given
configurator.AddCommand<HorseCommand>("horse"); var fixture = new CommandAppTester();
}); fixture.SetDefaultCommand<LionCommand>();
fixture.Configure(configurator =>
// When {
var result = fixture.Run("--help"); configurator.SetApplicationName("myapp");
});
// Then
return Verifier.Verify(result.Output); // When
} var result = fixture.Run("--help");
[Fact] // Then
[Expectation("RootExamples_Children")] return Verifier.Verify(result.Output);
public Task Should_Output_Root_Examples_Defined_On_Direct_Children_If_Root_Have_No_Examples() }
{
// Given [Fact]
var fixture = new CommandAppTester(); [Expectation("RootExamples")]
fixture.Configure(configurator => public Task Should_Output_Root_Examples_Defined_On_Root()
{ {
configurator.SetApplicationName("myapp"); // Given
configurator.AddCommand<DogCommand>("dog") var fixture = new CommandAppTester();
.WithExample(new[] { "dog", "--name", "Rufus", "--age", "12", "--good-boy" }); fixture.Configure(configurator =>
configurator.AddCommand<HorseCommand>("horse") {
.WithExample(new[] { "horse", "--name", "Brutus" }); configurator.SetApplicationName("myapp");
}); configurator.AddExample(new[] { "dog", "--name", "Rufus", "--age", "12", "--good-boy" });
configurator.AddExample(new[] { "horse", "--name", "Brutus" });
// When configurator.AddCommand<DogCommand>("dog");
var result = fixture.Run("--help"); configurator.AddCommand<HorseCommand>("horse");
});
// Then
return Verifier.Verify(result.Output); // When
} var result = fixture.Run("--help");
[Fact] // Then
[Expectation("RootExamples_Leafs")] return Verifier.Verify(result.Output);
public Task Should_Output_Root_Examples_Defined_On_Leaves_If_No_Other_Examples_Are_Found() }
{
// Given [Fact]
var fixture = new CommandAppTester(); [Expectation("RootExamples_Children")]
fixture.Configure(configurator => public Task Should_Output_Root_Examples_Defined_On_Direct_Children_If_Root_Have_No_Examples()
{ {
configurator.SetApplicationName("myapp"); // Given
configurator.AddBranch<AnimalSettings>("animal", animal => var fixture = new CommandAppTester();
{ fixture.Configure(configurator =>
animal.SetDescription("The animal command."); {
animal.AddCommand<DogCommand>("dog") configurator.SetApplicationName("myapp");
.WithExample(new[] { "animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy" }); configurator.AddCommand<DogCommand>("dog")
animal.AddCommand<HorseCommand>("horse") .WithExample(new[] { "dog", "--name", "Rufus", "--age", "12", "--good-boy" });
.WithExample(new[] { "animal", "horse", "--name", "Brutus" }); configurator.AddCommand<HorseCommand>("horse")
}); .WithExample(new[] { "horse", "--name", "Brutus" });
}); });
// When // When
var result = fixture.Run("--help"); var result = fixture.Run("--help");
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("CommandExamples")] [Expectation("RootExamples_Leafs")]
public Task Should_Only_Output_Command_Examples_Defined_On_Command() public Task Should_Output_Root_Examples_Defined_On_Leaves_If_No_Other_Examples_Are_Found()
{ {
// Given // Given
var fixture = new CommandAppTester(); var fixture = new CommandAppTester();
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
configurator.AddBranch<AnimalSettings>("animal", animal => configurator.AddBranch<AnimalSettings>("animal", animal =>
{ {
animal.SetDescription("The animal command."); animal.SetDescription("The animal command.");
animal.AddExample(new[] { "animal", "--help" }); animal.AddCommand<DogCommand>("dog")
.WithExample(new[] { "animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy" });
animal.AddCommand<DogCommand>("dog") animal.AddCommand<HorseCommand>("horse")
.WithExample(new[] { "animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy" }); .WithExample(new[] { "animal", "horse", "--name", "Brutus" });
animal.AddCommand<HorseCommand>("horse") });
.WithExample(new[] { "animal", "horse", "--name", "Brutus" }); });
});
}); // When
var result = fixture.Run("--help");
// When
var result = fixture.Run("animal", "--help"); // Then
return Verifier.Verify(result.Output);
// Then }
return Verifier.Verify(result.Output);
} [Fact]
[Expectation("CommandExamples")]
[Fact] public Task Should_Only_Output_Command_Examples_Defined_On_Command()
[Expectation("DefaultExamples")] {
public Task Should_Output_Root_Examples_If_Default_Command_Is_Specified() // Given
{ var fixture = new CommandAppTester();
// Given fixture.Configure(configurator =>
var fixture = new CommandAppTester(); {
fixture.SetDefaultCommand<LionCommand>(); configurator.SetApplicationName("myapp");
fixture.Configure(configurator => configurator.AddBranch<AnimalSettings>("animal", animal =>
{ {
configurator.SetApplicationName("myapp"); animal.SetDescription("The animal command.");
configurator.AddExample(new[] { "12", "-c", "3" }); animal.AddExample(new[] { "animal", "--help" });
});
animal.AddCommand<DogCommand>("dog")
// When .WithExample(new[] { "animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy" });
var result = fixture.Run("--help"); animal.AddCommand<HorseCommand>("horse")
.WithExample(new[] { "animal", "horse", "--name", "Brutus" });
// Then });
return Verifier.Verify(result.Output); });
}
// When
[Fact] var result = fixture.Run("animal", "--help");
[Expectation("NoDescription")]
public Task Should_Not_Show_Truncated_Command_Table_If_Commands_Are_Missing_Description() // Then
{ return Verifier.Verify(result.Output);
// Given }
var fixture = new CommandAppTester();
fixture.Configure(configurator => [Fact]
{ [Expectation("DefaultExamples")]
configurator.SetApplicationName("myapp"); public Task Should_Output_Root_Examples_If_Default_Command_Is_Specified()
configurator.AddCommand<NoDescriptionCommand>("bar"); {
}); // Given
var fixture = new CommandAppTester();
// When fixture.SetDefaultCommand<LionCommand>();
var result = fixture.Run("--help"); fixture.Configure(configurator =>
{
// Then configurator.SetApplicationName("myapp");
return Verifier.Verify(result.Output); configurator.AddExample(new[] { "12", "-c", "3" });
} });
[Fact] // When
[Expectation("ArgumentOrder")] var result = fixture.Run("--help");
public Task Should_List_Arguments_In_Correct_Order()
{ // Then
// Given return Verifier.Verify(result.Output);
var fixture = new CommandAppTester(); }
fixture.SetDefaultCommand<GenericCommand<ArgumentOrderSettings>>();
fixture.Configure(configurator => [Fact]
{ [Expectation("NoDescription")]
configurator.SetApplicationName("myapp"); public Task Should_Not_Show_Truncated_Command_Table_If_Commands_Are_Missing_Description()
}); {
// Given
// When var fixture = new CommandAppTester();
var result = fixture.Run("--help"); fixture.Configure(configurator =>
{
// Then configurator.SetApplicationName("myapp");
return Verifier.Verify(result.Output); configurator.AddCommand<NoDescriptionCommand>("bar");
} });
[Fact] // When
[Expectation("Hidden_Command_Options")] var result = fixture.Run("--help");
public Task Should_Not_Show_Hidden_Command_Options()
{ // Then
// Given return Verifier.Verify(result.Output);
var fixture = new CommandAppTester(); }
fixture.SetDefaultCommand<GenericCommand<HiddenOptionSettings>>();
fixture.Configure(configurator => [Fact]
{ [Expectation("ArgumentOrder")]
configurator.SetApplicationName("myapp"); public Task Should_List_Arguments_In_Correct_Order()
}); {
// Given
// When var fixture = new CommandAppTester();
var result = fixture.Run("--help"); fixture.SetDefaultCommand<GenericCommand<ArgumentOrderSettings>>();
fixture.Configure(configurator =>
// Then {
return Verifier.Verify(result.Output); configurator.SetApplicationName("myapp");
} });
}
} // When
var result = fixture.Run("--help");
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Hidden_Command_Options")]
public Task Should_Not_Show_Hidden_Command_Options()
{
// Given
var fixture = new CommandAppTester();
fixture.SetDefaultCommand<GenericCommand<HiddenOptionSettings>>();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
});
// When
var result = fixture.Run("--help");
// Then
return Verifier.Verify(result.Output);
}
}
}