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);
}
}