namespace Spectre.Console.Testing; /// /// A test harness. /// public sealed class CommandAppTester { private Action? _appConfiguration; private Action? _configuration; /// /// Gets the test console used by both the CommandAppTester and CommandApp. /// public TestConsole Console { get; } /// /// Gets or sets the Registrar to use in the CommandApp. /// public ITypeRegistrar? Registrar { get; set; } /// /// Gets or sets the settings for the . /// public CommandAppTesterSettings TestSettings { get; set; } /// /// Initializes a new instance of the class. /// /// The registrar. /// The settings. /// The test console that overrides the default one. public CommandAppTester( ITypeRegistrar? registrar = null, CommandAppTesterSettings? settings = null, TestConsole? console = null) { Registrar = registrar; TestSettings = settings ?? new CommandAppTesterSettings(); Console = console ?? new TestConsole().Width(int.MaxValue); } /// /// Initializes a new instance of the class. /// /// The settings. public CommandAppTester(CommandAppTesterSettings settings) { TestSettings = settings; Console = new TestConsole().Width(int.MaxValue); } /// /// Sets the default command. /// /// The optional default command description. /// The optional default command data. /// The default command type. public void SetDefaultCommand(string? description = null, object? data = null) where T : class, ICommand { _appConfiguration = (app) => { var defaultCommandBuilder = app.SetDefaultCommand(); if (description != null) { defaultCommandBuilder.WithDescription(description); } if (data != null) { defaultCommandBuilder.WithData(data); } }; } /// /// Configures the command application. /// /// The configuration action. public void Configure(Action action) { if (_configuration != null) { throw new InvalidOperationException("The command app harnest have already been configured."); } _configuration = action; } /// /// Runs the command application and expects an exception of a specific type to be thrown. /// /// The expected exception type. /// The arguments. /// The information about the failure. public CommandAppFailure RunAndCatch(params string[] args) where T : Exception { try { Run(args, Console, c => c.PropagateExceptions()); throw new InvalidOperationException("Expected an exception to be thrown, but there was none."); } catch (T ex) { if (ex is CommandAppException commandAppException && commandAppException.Pretty != null) { Console.Write(commandAppException.Pretty); } else { Console.WriteLine(ex.Message); } return new CommandAppFailure(ex, Console.Output); } catch (Exception ex) { throw new InvalidOperationException( $"Expected an exception of type '{typeof(T).FullName}' to be thrown, " + $"but received {ex.GetType().FullName}."); } } /// /// Runs the command application. /// /// The arguments. /// The result. public CommandAppResult Run(params string[] args) { return Run(args, Console); } private CommandAppResult Run(string[] args, TestConsole console, Action? config = null) { CommandContext? context = null; CommandSettings? settings = null; var app = new CommandApp(Registrar); _appConfiguration?.Invoke(app); if (_configuration != null) { app.Configure(_configuration); } if (config != null) { app.Configure(config); } app.Configure(c => c.ConfigureConsole(console)); app.Configure(c => c.SetInterceptor(new CallbackCommandInterceptor((ctx, s) => { context = ctx; settings = s; }))); var result = app.Run(args); var output = console.Output.NormalizeLineEndings(); output = TestSettings.TrimConsoleOutput ? output.TrimLines().Trim() : output; return new CommandAppResult(result, output, context, settings); } /// /// Runs the command application asynchronously. /// /// The arguments. /// The result. public async Task RunAsync(params string[] args) { return await RunAsync(args, Console); } private async Task RunAsync(string[] args, TestConsole console, Action? config = null) { CommandContext? context = null; CommandSettings? settings = null; var app = new CommandApp(Registrar); _appConfiguration?.Invoke(app); if (_configuration != null) { app.Configure(_configuration); } if (config != null) { app.Configure(config); } app.Configure(c => c.ConfigureConsole(console)); app.Configure(c => c.SetInterceptor(new CallbackCommandInterceptor((ctx, s) => { context = ctx; settings = s; }))); var result = await app.RunAsync(args); var output = console.Output.NormalizeLineEndings(); output = TestSettings.TrimConsoleOutput ? output.TrimLines().Trim() : output; return new CommandAppResult(result, output, context, settings); } }