From f6ef6cd4c0a52719d87788d284b913c0c3c0e948 Mon Sep 17 00:00:00 2001 From: David Fallah Date: Tue, 4 Jan 2022 20:12:17 +0000 Subject: [PATCH] Fix ordering of parameters within command help usage (#118) --- CliFx.Tests/HelpTextSpecs.cs | 50 ++++++++++++++++++++++ CliFx.Tests/Utils/DynamicCommandBuilder.cs | 6 +-- CliFx/Schema/CommandSchema.cs | 1 + 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/CliFx.Tests/HelpTextSpecs.cs b/CliFx.Tests/HelpTextSpecs.cs index d9fb170..7e5304d 100644 --- a/CliFx.Tests/HelpTextSpecs.cs +++ b/CliFx.Tests/HelpTextSpecs.cs @@ -223,6 +223,56 @@ public class NamedChildCommand : ICommand stdErr.Should().NotBeNullOrWhiteSpace(); } + // Regression test for #117 + [Fact] + public async Task Help_text_lists_parameters_in_specified_order() + { + // Arrange + var commandType = DynamicCommandBuilder.Compile( + // language=cs + @" +public abstract class CommandBase : ICommand +{ + [CommandParameter(0)] + public string Foo { get; set; } + + public abstract ValueTask ExecuteAsync(IConsole console); +} + +[Command] +public class Command : CommandBase +{ + [CommandParameter(1)] + public string Bar { get; set; } + + [CommandParameter(2)] + public IReadOnlyList Baz { get; set; } + + public override ValueTask ExecuteAsync(IConsole console) => default; +} +"); + + var application = new CliApplicationBuilder() + .AddCommand(commandType) + .UseConsole(FakeConsole) + .Build(); + + // Act + var exitCode = await application.RunAsync( + new[] { "--help" }, + new Dictionary() + ); + + var stdOut = FakeConsole.ReadOutputString(); + + // Assert + exitCode.Should().Be(0); + stdOut.Should().ContainAllInOrder( + "USAGE", + "", "", "" + ); + } + [Fact] public async Task Help_text_shows_application_metadata() { diff --git a/CliFx.Tests/Utils/DynamicCommandBuilder.cs b/CliFx.Tests/Utils/DynamicCommandBuilder.cs index 588f251..3a18b58 100644 --- a/CliFx.Tests/Utils/DynamicCommandBuilder.cs +++ b/CliFx.Tests/Utils/DynamicCommandBuilder.cs @@ -106,13 +106,13 @@ internal static class DynamicCommandBuilder // Return all defined commands var commandTypes = generatedAssembly .GetTypes() - .Where(t => t.IsAssignableTo(typeof(ICommand))) + .Where(t => t.IsAssignableTo(typeof(ICommand)) && !t.IsAbstract) .ToArray(); if (commandTypes.Length <= 0) { throw new InvalidOperationException( - "There are no command definitions in the provide source code." + "There are no command definitions in the provided source code." ); } @@ -126,7 +126,7 @@ internal static class DynamicCommandBuilder if (commandTypes.Count > 1) { throw new InvalidOperationException( - "There are more than one command definitions in the provide source code." + "There are more than one command definitions in the provided source code." ); } diff --git a/CliFx/Schema/CommandSchema.cs b/CliFx/Schema/CommandSchema.cs index 61d518c..d71687e 100644 --- a/CliFx/Schema/CommandSchema.cs +++ b/CliFx/Schema/CommandSchema.cs @@ -90,6 +90,7 @@ internal partial class CommandSchema var parameterSchemas = type.GetProperties() .Select(ParameterSchema.TryResolve) .Where(p => p is not null) + .OrderBy(p => p!.Order) .ToArray(); var optionSchemas = type.GetProperties()