mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-07-06 04:28:15 +08:00
Use file scoped namespace declarations
This commit is contained in:

committed by
Phil Scott

parent
1dbaf50935
commit
ec1188b837
@ -1,28 +1,27 @@
|
||||
using System;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
namespace Spectre.Console.Testing
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="ICommandInterceptor"/> that triggers a callback when invoked.
|
||||
/// </summary>
|
||||
public sealed class CallbackCommandInterceptor : ICommandInterceptor
|
||||
{
|
||||
private readonly Action<CommandContext, CommandSettings> _callback;
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="ICommandInterceptor"/> that triggers a callback when invoked.
|
||||
/// Initializes a new instance of the <see cref="CallbackCommandInterceptor"/> class.
|
||||
/// </summary>
|
||||
public sealed class CallbackCommandInterceptor : ICommandInterceptor
|
||||
/// <param name="callback">The callback to call when the interceptor is invoked.</param>
|
||||
public CallbackCommandInterceptor(Action<CommandContext, CommandSettings> callback)
|
||||
{
|
||||
private readonly Action<CommandContext, CommandSettings> _callback;
|
||||
_callback = callback ?? throw new ArgumentNullException(nameof(callback));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CallbackCommandInterceptor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="callback">The callback to call when the interceptor is invoked.</param>
|
||||
public CallbackCommandInterceptor(Action<CommandContext, CommandSettings> callback)
|
||||
{
|
||||
_callback = callback ?? throw new ArgumentNullException(nameof(callback));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Intercept(CommandContext context, CommandSettings settings)
|
||||
{
|
||||
_callback(context, settings);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void Intercept(CommandContext context, CommandSettings settings)
|
||||
{
|
||||
_callback(context, settings);
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,28 @@
|
||||
using System;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
namespace Spectre.Console.Testing
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a <see cref="CommandApp"/> runtime failure.
|
||||
/// </summary>
|
||||
public sealed class CommandAppFailure
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="CommandApp"/> runtime failure.
|
||||
/// Gets the exception that was thrown.
|
||||
/// </summary>
|
||||
public sealed class CommandAppFailure
|
||||
public Exception Exception { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the console output.
|
||||
/// </summary>
|
||||
public string Output { get; }
|
||||
|
||||
internal CommandAppFailure(Exception exception, string output)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the exception that was thrown.
|
||||
/// </summary>
|
||||
public Exception Exception { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the console output.
|
||||
/// </summary>
|
||||
public string Output { get; }
|
||||
|
||||
internal CommandAppFailure(Exception exception, string output)
|
||||
{
|
||||
Exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
||||
Output = output.NormalizeLineEndings()
|
||||
.TrimLines()
|
||||
.Trim();
|
||||
}
|
||||
Exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
||||
Output = output.NormalizeLineEndings()
|
||||
.TrimLines()
|
||||
.Trim();
|
||||
}
|
||||
}
|
||||
|
@ -1,43 +1,42 @@
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
namespace Spectre.Console.Testing
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the result of a completed <see cref="CommandApp"/> run.
|
||||
/// </summary>
|
||||
public sealed class CommandAppResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the result of a completed <see cref="CommandApp"/> run.
|
||||
/// Gets the exit code.
|
||||
/// </summary>
|
||||
public sealed class CommandAppResult
|
||||
public int ExitCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the console output.
|
||||
/// </summary>
|
||||
public string Output { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command context.
|
||||
/// </summary>
|
||||
public CommandContext? Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command settings.
|
||||
/// </summary>
|
||||
public CommandSettings? Settings { get; }
|
||||
|
||||
internal CommandAppResult(int exitCode, string output, CommandContext? context, CommandSettings? settings)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the exit code.
|
||||
/// </summary>
|
||||
public int ExitCode { get; }
|
||||
ExitCode = exitCode;
|
||||
Output = output ?? string.Empty;
|
||||
Context = context;
|
||||
Settings = settings;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the console output.
|
||||
/// </summary>
|
||||
public string Output { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command context.
|
||||
/// </summary>
|
||||
public CommandContext? Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command settings.
|
||||
/// </summary>
|
||||
public CommandSettings? Settings { get; }
|
||||
|
||||
internal CommandAppResult(int exitCode, string output, CommandContext? context, CommandSettings? settings)
|
||||
{
|
||||
ExitCode = exitCode;
|
||||
Output = output ?? string.Empty;
|
||||
Context = context;
|
||||
Settings = settings;
|
||||
|
||||
Output = Output
|
||||
.NormalizeLineEndings()
|
||||
.TrimLines()
|
||||
.Trim();
|
||||
}
|
||||
Output = Output
|
||||
.NormalizeLineEndings()
|
||||
.TrimLines()
|
||||
.Trim();
|
||||
}
|
||||
}
|
||||
|
@ -1,135 +1,134 @@
|
||||
using System;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
namespace Spectre.Console.Testing
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="CommandApp"/> test harness.
|
||||
/// </summary>
|
||||
public sealed class CommandAppTester
|
||||
{
|
||||
private Action<CommandApp>? _appConfiguration;
|
||||
private Action<IConfigurator>? _configuration;
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="CommandApp"/> test harness.
|
||||
/// Initializes a new instance of the <see cref="CommandAppTester"/> class.
|
||||
/// </summary>
|
||||
public sealed class CommandAppTester
|
||||
/// <param name="registrar">The registrar.</param>
|
||||
public CommandAppTester(ITypeRegistrar? registrar = null)
|
||||
{
|
||||
private Action<CommandApp>? _appConfiguration;
|
||||
private Action<IConfigurator>? _configuration;
|
||||
Registrar = registrar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CommandAppTester"/> class.
|
||||
/// </summary>
|
||||
/// <param name="registrar">The registrar.</param>
|
||||
public CommandAppTester(ITypeRegistrar? registrar = null)
|
||||
/// <summary>
|
||||
/// Gets or sets the Registrar to use in the CommandApp.
|
||||
/// </summary>
|
||||
public ITypeRegistrar? Registrar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default command.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The default command type.</typeparam>
|
||||
public void SetDefaultCommand<T>()
|
||||
where T : class, ICommand
|
||||
{
|
||||
_appConfiguration = (app) => app.SetDefaultCommand<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the command application.
|
||||
/// </summary>
|
||||
/// <param name="action">The configuration action.</param>
|
||||
public void Configure(Action<IConfigurator> action)
|
||||
{
|
||||
if (_configuration != null)
|
||||
{
|
||||
Registrar = registrar;
|
||||
throw new InvalidOperationException("The command app harnest have already been configured.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Registrar to use in the CommandApp.
|
||||
/// </summary>
|
||||
public ITypeRegistrar? Registrar { get; set; }
|
||||
_configuration = action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default command.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The default command type.</typeparam>
|
||||
public void SetDefaultCommand<T>()
|
||||
where T : class, ICommand
|
||||
/// <summary>
|
||||
/// Runs the command application and expects an exception of a specific type to be thrown.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The expected exception type.</typeparam>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <returns>The information about the failure.</returns>
|
||||
public CommandAppFailure RunAndCatch<T>(params string[] args)
|
||||
where T : Exception
|
||||
{
|
||||
var console = new TestConsole().Width(int.MaxValue);
|
||||
|
||||
try
|
||||
{
|
||||
_appConfiguration = (app) => app.SetDefaultCommand<T>();
|
||||
Run(args, console, c => c.PropagateExceptions());
|
||||
throw new InvalidOperationException("Expected an exception to be thrown, but there was none.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the command application.
|
||||
/// </summary>
|
||||
/// <param name="action">The configuration action.</param>
|
||||
public void Configure(Action<IConfigurator> action)
|
||||
catch (T ex)
|
||||
{
|
||||
if (_configuration != null)
|
||||
if (ex is CommandAppException commandAppException && commandAppException.Pretty != null)
|
||||
{
|
||||
throw new InvalidOperationException("The command app harnest have already been configured.");
|
||||
console.Write(commandAppException.Pretty);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.WriteLine(ex.Message);
|
||||
}
|
||||
|
||||
_configuration = action;
|
||||
return new CommandAppFailure(ex, console.Output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the command application and expects an exception of a specific type to be thrown.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The expected exception type.</typeparam>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <returns>The information about the failure.</returns>
|
||||
public CommandAppFailure RunAndCatch<T>(params string[] args)
|
||||
where T : Exception
|
||||
catch (Exception ex)
|
||||
{
|
||||
var console = new TestConsole().Width(int.MaxValue);
|
||||
|
||||
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}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the command application.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <returns>The result.</returns>
|
||||
public CommandAppResult Run(params string[] args)
|
||||
{
|
||||
var console = new TestConsole().Width(int.MaxValue);
|
||||
return Run(args, console);
|
||||
}
|
||||
|
||||
private CommandAppResult Run(string[] args, TestConsole console, Action<IConfigurator>? 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()
|
||||
.TrimLines()
|
||||
.Trim();
|
||||
|
||||
return new CommandAppResult(result, output, context, settings);
|
||||
throw new InvalidOperationException(
|
||||
$"Expected an exception of type '{typeof(T).FullName}' to be thrown, "
|
||||
+ $"but received {ex.GetType().FullName}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the command application.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <returns>The result.</returns>
|
||||
public CommandAppResult Run(params string[] args)
|
||||
{
|
||||
var console = new TestConsole().Width(int.MaxValue);
|
||||
return Run(args, console);
|
||||
}
|
||||
|
||||
private CommandAppResult Run(string[] args, TestConsole console, Action<IConfigurator>? 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()
|
||||
.TrimLines()
|
||||
.Trim();
|
||||
|
||||
return new CommandAppResult(result, output, context, settings);
|
||||
}
|
||||
}
|
||||
|
@ -1,191 +1,190 @@
|
||||
using System;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
namespace Spectre.Console.Testing
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
/// <summary>
|
||||
/// This is a utility class for implementors of
|
||||
/// <see cref="ITypeRegistrar"/> and corresponding <see cref="ITypeResolver"/>.
|
||||
/// </summary>
|
||||
public sealed class TypeRegistrarBaseTests
|
||||
{
|
||||
private readonly Func<ITypeRegistrar> _registrarFactory;
|
||||
|
||||
/// <summary>
|
||||
/// This is a utility class for implementors of
|
||||
/// <see cref="ITypeRegistrar"/> and corresponding <see cref="ITypeResolver"/>.
|
||||
/// Initializes a new instance of the <see cref="TypeRegistrarBaseTests"/> class.
|
||||
/// </summary>
|
||||
public sealed class TypeRegistrarBaseTests
|
||||
/// <param name="registrarFactory">The factory to create a new, clean <see cref="ITypeRegistrar"/> to be used for each test.</param>
|
||||
public TypeRegistrarBaseTests(Func<ITypeRegistrar> registrarFactory)
|
||||
{
|
||||
private readonly Func<ITypeRegistrar> _registrarFactory;
|
||||
_registrarFactory = registrarFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TypeRegistrarBaseTests"/> class.
|
||||
/// </summary>
|
||||
/// <param name="registrarFactory">The factory to create a new, clean <see cref="ITypeRegistrar"/> to be used for each test.</param>
|
||||
public TypeRegistrarBaseTests(Func<ITypeRegistrar> registrarFactory)
|
||||
/// <summary>
|
||||
/// Runs all tests.
|
||||
/// </summary>
|
||||
/// <exception cref="TestFailedException">This exception is raised, if a test fails.</exception>
|
||||
public void RunAllTests()
|
||||
{
|
||||
var testCases = new Action<ITypeRegistrar>[]
|
||||
{
|
||||
_registrarFactory = registrarFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs all tests.
|
||||
/// </summary>
|
||||
/// <exception cref="TestFailedException">This exception is raised, if a test fails.</exception>
|
||||
public void RunAllTests()
|
||||
{
|
||||
var testCases = new Action<ITypeRegistrar>[]
|
||||
{
|
||||
RegistrationsCanBeResolved,
|
||||
InstanceRegistrationsCanBeResolved,
|
||||
LazyRegistrationsCanBeResolved,
|
||||
ResolvingNotRegisteredServiceReturnsNull,
|
||||
ResolvingNullTypeReturnsNull,
|
||||
};
|
||||
};
|
||||
|
||||
foreach (var test in testCases)
|
||||
{
|
||||
test(_registrarFactory());
|
||||
}
|
||||
}
|
||||
|
||||
private static void ResolvingNullTypeReturnsNull(ITypeRegistrar registrar)
|
||||
foreach (var test in testCases)
|
||||
{
|
||||
// Given no registration
|
||||
var resolver = registrar.Build();
|
||||
|
||||
try
|
||||
{
|
||||
// When
|
||||
var actual = resolver.Resolve(null);
|
||||
|
||||
// Then
|
||||
if (actual != null)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
$"Expected the resolver to resolve null, since null was requested as the service type. Actually resolved {actual.GetType().Name}.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
$"Expected the resolver not to throw, but caught {ex.GetType().Name}.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ResolvingNotRegisteredServiceReturnsNull(ITypeRegistrar registrar)
|
||||
{
|
||||
// Given no registration
|
||||
var resolver = registrar.Build();
|
||||
|
||||
try
|
||||
{
|
||||
// When
|
||||
var actual = resolver.Resolve(typeof(IMockService));
|
||||
|
||||
// Then
|
||||
if (actual != null)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
$"Expected the resolver to resolve null, since no service was registered. Actually resolved {actual.GetType().Name}.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
$"Expected the resolver not to throw, but caught {ex.GetType().Name}.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegistrationsCanBeResolved(ITypeRegistrar registrar)
|
||||
{
|
||||
// Given
|
||||
registrar.Register(typeof(IMockService), typeof(MockService));
|
||||
var resolver = registrar.Build();
|
||||
|
||||
// When
|
||||
var actual = resolver.Resolve(typeof(IMockService));
|
||||
|
||||
// Then
|
||||
if (actual == null)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
$"Expected the resolver to resolve an instance of {nameof(MockService)}. Actually resolved null.");
|
||||
}
|
||||
|
||||
if (actual is not MockService)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
$"Expected the resolver to resolve an instance of {nameof(MockService)}. Actually resolved {actual.GetType().Name}.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void InstanceRegistrationsCanBeResolved(ITypeRegistrar registrar)
|
||||
{
|
||||
// Given
|
||||
var instance = new MockService();
|
||||
registrar.RegisterInstance(typeof(IMockService), instance);
|
||||
var resolver = registrar.Build();
|
||||
|
||||
// When
|
||||
var actual = resolver.Resolve(typeof(IMockService));
|
||||
|
||||
// Then
|
||||
if (!ReferenceEquals(actual, instance))
|
||||
{
|
||||
throw new TestFailedException(
|
||||
"Expected the resolver to resolve exactly the registered instance.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void LazyRegistrationsCanBeResolved(ITypeRegistrar registrar)
|
||||
{
|
||||
// Given
|
||||
var instance = new MockService();
|
||||
var factoryCalled = false;
|
||||
registrar.RegisterLazy(typeof(IMockService), () =>
|
||||
{
|
||||
factoryCalled = true;
|
||||
return instance;
|
||||
});
|
||||
var resolver = registrar.Build();
|
||||
|
||||
// When
|
||||
var actual = resolver.Resolve(typeof(IMockService));
|
||||
|
||||
// Then
|
||||
if (!factoryCalled)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
"Expected the factory to be called, to resolve the lazy registration.");
|
||||
}
|
||||
|
||||
if (!ReferenceEquals(actual, instance))
|
||||
{
|
||||
throw new TestFailedException(
|
||||
"Expected the resolver to return exactly the result of the lazy-registered factory.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// internal use only.
|
||||
/// </summary>
|
||||
private interface IMockService
|
||||
{
|
||||
}
|
||||
|
||||
private class MockService : IMockService
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception, to be raised when a test fails.
|
||||
/// </summary>
|
||||
public sealed class TestFailedException : Exception
|
||||
{
|
||||
/// <inheritdoc cref="Exception" />
|
||||
public TestFailedException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Exception" />
|
||||
public TestFailedException(string message, Exception inner)
|
||||
: base(message, inner)
|
||||
{
|
||||
}
|
||||
test(_registrarFactory());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ResolvingNullTypeReturnsNull(ITypeRegistrar registrar)
|
||||
{
|
||||
// Given no registration
|
||||
var resolver = registrar.Build();
|
||||
|
||||
try
|
||||
{
|
||||
// When
|
||||
var actual = resolver.Resolve(null);
|
||||
|
||||
// Then
|
||||
if (actual != null)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
$"Expected the resolver to resolve null, since null was requested as the service type. Actually resolved {actual.GetType().Name}.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
$"Expected the resolver not to throw, but caught {ex.GetType().Name}.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ResolvingNotRegisteredServiceReturnsNull(ITypeRegistrar registrar)
|
||||
{
|
||||
// Given no registration
|
||||
var resolver = registrar.Build();
|
||||
|
||||
try
|
||||
{
|
||||
// When
|
||||
var actual = resolver.Resolve(typeof(IMockService));
|
||||
|
||||
// Then
|
||||
if (actual != null)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
$"Expected the resolver to resolve null, since no service was registered. Actually resolved {actual.GetType().Name}.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
$"Expected the resolver not to throw, but caught {ex.GetType().Name}.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegistrationsCanBeResolved(ITypeRegistrar registrar)
|
||||
{
|
||||
// Given
|
||||
registrar.Register(typeof(IMockService), typeof(MockService));
|
||||
var resolver = registrar.Build();
|
||||
|
||||
// When
|
||||
var actual = resolver.Resolve(typeof(IMockService));
|
||||
|
||||
// Then
|
||||
if (actual == null)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
$"Expected the resolver to resolve an instance of {nameof(MockService)}. Actually resolved null.");
|
||||
}
|
||||
|
||||
if (actual is not MockService)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
$"Expected the resolver to resolve an instance of {nameof(MockService)}. Actually resolved {actual.GetType().Name}.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void InstanceRegistrationsCanBeResolved(ITypeRegistrar registrar)
|
||||
{
|
||||
// Given
|
||||
var instance = new MockService();
|
||||
registrar.RegisterInstance(typeof(IMockService), instance);
|
||||
var resolver = registrar.Build();
|
||||
|
||||
// When
|
||||
var actual = resolver.Resolve(typeof(IMockService));
|
||||
|
||||
// Then
|
||||
if (!ReferenceEquals(actual, instance))
|
||||
{
|
||||
throw new TestFailedException(
|
||||
"Expected the resolver to resolve exactly the registered instance.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void LazyRegistrationsCanBeResolved(ITypeRegistrar registrar)
|
||||
{
|
||||
// Given
|
||||
var instance = new MockService();
|
||||
var factoryCalled = false;
|
||||
registrar.RegisterLazy(typeof(IMockService), () =>
|
||||
{
|
||||
factoryCalled = true;
|
||||
return instance;
|
||||
});
|
||||
var resolver = registrar.Build();
|
||||
|
||||
// When
|
||||
var actual = resolver.Resolve(typeof(IMockService));
|
||||
|
||||
// Then
|
||||
if (!factoryCalled)
|
||||
{
|
||||
throw new TestFailedException(
|
||||
"Expected the factory to be called, to resolve the lazy registration.");
|
||||
}
|
||||
|
||||
if (!ReferenceEquals(actual, instance))
|
||||
{
|
||||
throw new TestFailedException(
|
||||
"Expected the resolver to return exactly the result of the lazy-registered factory.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// internal use only.
|
||||
/// </summary>
|
||||
private interface IMockService
|
||||
{
|
||||
}
|
||||
|
||||
private class MockService : IMockService
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception, to be raised when a test fails.
|
||||
/// </summary>
|
||||
public sealed class TestFailedException : Exception
|
||||
{
|
||||
/// <inheritdoc cref="Exception" />
|
||||
public TestFailedException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Exception" />
|
||||
public TestFailedException(string message, Exception inner)
|
||||
: base(message, inner)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spectre.Console.Testing
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
/// <summary>
|
||||
/// Contains extensions for <see cref="string"/>.
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains extensions for <see cref="string"/>.
|
||||
/// Returns a new string with all lines trimmed of trailing whitespace.
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
/// <param name="value">The string to trim.</param>
|
||||
/// <returns>A new string with all lines trimmed of trailing whitespace.</returns>
|
||||
public static string TrimLines(this string value)
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a new string with all lines trimmed of trailing whitespace.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to trim.</param>
|
||||
/// <returns>A new string with all lines trimmed of trailing whitespace.</returns>
|
||||
public static string TrimLines(this string value)
|
||||
if (value is null)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var result = new List<string>();
|
||||
foreach (var line in value.NormalizeLineEndings().Split(new[] { '\n' }))
|
||||
{
|
||||
result.Add(line.TrimEnd());
|
||||
}
|
||||
|
||||
return string.Join("\n", result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new string with normalized line endings.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to normalize line endings for.</param>
|
||||
/// <returns>A new string with normalized line endings.</returns>
|
||||
public static string NormalizeLineEndings(this string value)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
value = value.Replace("\r\n", "\n");
|
||||
return value.Replace("\r", string.Empty);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var result = new List<string>();
|
||||
foreach (var line in value.NormalizeLineEndings().Split(new[] { '\n' }))
|
||||
{
|
||||
result.Add(line.TrimEnd());
|
||||
}
|
||||
|
||||
return string.Join("\n", result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new string with normalized line endings.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to normalize line endings for.</param>
|
||||
/// <returns>A new string with normalized line endings.</returns>
|
||||
public static string NormalizeLineEndings(this string value)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
value = value.Replace("\r\n", "\n");
|
||||
return value.Replace("\r", string.Empty);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,24 @@
|
||||
namespace Spectre.Console.Testing
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
/// <summary>
|
||||
/// Contains extensions for <see cref="Style"/>.
|
||||
/// </summary>
|
||||
public static class StyleExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains extensions for <see cref="Style"/>.
|
||||
/// Sets the foreground or background color of the specified style.
|
||||
/// </summary>
|
||||
public static class StyleExtensions
|
||||
/// <param name="style">The style.</param>
|
||||
/// <param name="color">The color.</param>
|
||||
/// <param name="foreground">Whether or not to set the foreground color.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Style SetColor(this Style style, Color color, bool foreground)
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the foreground or background color of the specified style.
|
||||
/// </summary>
|
||||
/// <param name="style">The style.</param>
|
||||
/// <param name="color">The color.</param>
|
||||
/// <param name="foreground">Whether or not to set the foreground color.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Style SetColor(this Style style, Color color, bool foreground)
|
||||
if (foreground)
|
||||
{
|
||||
if (foreground)
|
||||
{
|
||||
return style.Foreground(color);
|
||||
}
|
||||
|
||||
return style.Background(color);
|
||||
return style.Foreground(color);
|
||||
}
|
||||
|
||||
return style.Background(color);
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,16 @@
|
||||
namespace Spectre.Console.Testing
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
internal sealed class NoopCursor : IAnsiConsoleCursor
|
||||
{
|
||||
internal sealed class NoopCursor : IAnsiConsoleCursor
|
||||
public void Move(CursorDirection direction, int steps)
|
||||
{
|
||||
public void Move(CursorDirection direction, int steps)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPosition(int column, int line)
|
||||
{
|
||||
}
|
||||
public void SetPosition(int column, int line)
|
||||
{
|
||||
}
|
||||
|
||||
public void Show(bool show)
|
||||
{
|
||||
}
|
||||
public void Show(bool show)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,17 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spectre.Console.Testing
|
||||
{
|
||||
internal sealed class NoopExclusivityMode : IExclusivityMode
|
||||
{
|
||||
public T Run<T>(Func<T> func)
|
||||
{
|
||||
return func();
|
||||
}
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
public async Task<T> RunAsync<T>(Func<Task<T>> func)
|
||||
{
|
||||
return await func().ConfigureAwait(false);
|
||||
}
|
||||
internal sealed class NoopExclusivityMode : IExclusivityMode
|
||||
{
|
||||
public T Run<T>(Func<T> func)
|
||||
{
|
||||
return func();
|
||||
}
|
||||
|
||||
public async Task<T> RunAsync<T>(Func<Task<T>> func)
|
||||
{
|
||||
return await func().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
@ -1,40 +1,39 @@
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console.Testing
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
/// <summary>
|
||||
/// Represents fake capabilities useful in tests.
|
||||
/// </summary>
|
||||
public sealed class TestCapabilities : IReadOnlyCapabilities
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ColorSystem ColorSystem { get; set; } = ColorSystem.TrueColor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Ansi { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Links { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Legacy { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsTerminal { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Interactive { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Unicode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Represents fake capabilities useful in tests.
|
||||
/// Creates a <see cref="RenderContext"/> with the same capabilities as this instace.
|
||||
/// </summary>
|
||||
public sealed class TestCapabilities : IReadOnlyCapabilities
|
||||
/// <returns>A <see cref="RenderContext"/> with the same capabilities as this instace.</returns>
|
||||
public RenderContext CreateRenderContext()
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ColorSystem ColorSystem { get; set; } = ColorSystem.TrueColor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Ansi { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Links { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Legacy { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsTerminal { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Interactive { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Unicode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="RenderContext"/> with the same capabilities as this instace.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="RenderContext"/> with the same capabilities as this instace.</returns>
|
||||
public RenderContext CreateRenderContext()
|
||||
{
|
||||
return new RenderContext(this);
|
||||
}
|
||||
return new RenderContext(this);
|
||||
}
|
||||
}
|
||||
|
@ -3,120 +3,119 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console.Testing
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
/// <summary>
|
||||
/// A testable console.
|
||||
/// </summary>
|
||||
public sealed class TestConsole : IAnsiConsole, IDisposable
|
||||
{
|
||||
private readonly IAnsiConsole _console;
|
||||
private readonly StringWriter _writer;
|
||||
private IAnsiConsoleCursor? _cursor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Profile Profile => _console.Profile;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IExclusivityMode ExclusivityMode => _console.ExclusivityMode;
|
||||
|
||||
/// <summary>
|
||||
/// A testable console.
|
||||
/// Gets the console input.
|
||||
/// </summary>
|
||||
public sealed class TestConsole : IAnsiConsole, IDisposable
|
||||
public TestConsoleInput Input { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public RenderPipeline Pipeline => _console.Pipeline;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IAnsiConsoleCursor Cursor => _cursor ?? _console.Cursor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
IAnsiConsoleInput IAnsiConsole.Input => Input;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the console output.
|
||||
/// </summary>
|
||||
public string Output => _writer.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the console output lines.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> Lines => Output.NormalizeLineEndings().TrimEnd('\n').Split(new char[] { '\n' });
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not VT/ANSI sequences
|
||||
/// should be emitted to the console.
|
||||
/// </summary>
|
||||
public bool EmitAnsiSequences { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestConsole"/> class.
|
||||
/// </summary>
|
||||
public TestConsole()
|
||||
{
|
||||
private readonly IAnsiConsole _console;
|
||||
private readonly StringWriter _writer;
|
||||
private IAnsiConsoleCursor? _cursor;
|
||||
_writer = new StringWriter();
|
||||
_cursor = new NoopCursor();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Profile Profile => _console.Profile;
|
||||
Input = new TestConsoleInput();
|
||||
EmitAnsiSequences = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IExclusivityMode ExclusivityMode => _console.ExclusivityMode;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the console input.
|
||||
/// </summary>
|
||||
public TestConsoleInput Input { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public RenderPipeline Pipeline => _console.Pipeline;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IAnsiConsoleCursor Cursor => _cursor ?? _console.Cursor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
IAnsiConsoleInput IAnsiConsole.Input => Input;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the console output.
|
||||
/// </summary>
|
||||
public string Output => _writer.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the console output lines.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> Lines => Output.NormalizeLineEndings().TrimEnd('\n').Split(new char[] { '\n' });
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not VT/ANSI sequences
|
||||
/// should be emitted to the console.
|
||||
/// </summary>
|
||||
public bool EmitAnsiSequences { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestConsole"/> class.
|
||||
/// </summary>
|
||||
public TestConsole()
|
||||
var factory = new AnsiConsoleFactory();
|
||||
_console = factory.Create(new AnsiConsoleSettings
|
||||
{
|
||||
_writer = new StringWriter();
|
||||
_cursor = new NoopCursor();
|
||||
|
||||
Input = new TestConsoleInput();
|
||||
EmitAnsiSequences = false;
|
||||
|
||||
var factory = new AnsiConsoleFactory();
|
||||
_console = factory.Create(new AnsiConsoleSettings
|
||||
Ansi = AnsiSupport.Yes,
|
||||
ColorSystem = (ColorSystemSupport)ColorSystem.TrueColor,
|
||||
Out = new AnsiConsoleOutput(_writer),
|
||||
Interactive = InteractionSupport.No,
|
||||
ExclusivityMode = new NoopExclusivityMode(),
|
||||
Enrichment = new ProfileEnrichment
|
||||
{
|
||||
Ansi = AnsiSupport.Yes,
|
||||
ColorSystem = (ColorSystemSupport)ColorSystem.TrueColor,
|
||||
Out = new AnsiConsoleOutput(_writer),
|
||||
Interactive = InteractionSupport.No,
|
||||
ExclusivityMode = new NoopExclusivityMode(),
|
||||
Enrichment = new ProfileEnrichment
|
||||
UseDefaultEnrichers = false,
|
||||
},
|
||||
});
|
||||
|
||||
_console.Profile.Width = 80;
|
||||
_console.Profile.Height = 24;
|
||||
_console.Profile.Capabilities.Ansi = true;
|
||||
_console.Profile.Capabilities.Unicode = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
_writer.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Clear(bool home)
|
||||
{
|
||||
_console.Clear(home);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Write(IRenderable renderable)
|
||||
{
|
||||
if (EmitAnsiSequences)
|
||||
{
|
||||
_console.Write(renderable);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var segment in renderable.GetSegments(this))
|
||||
{
|
||||
if (segment.IsControlCode)
|
||||
{
|
||||
UseDefaultEnrichers = false,
|
||||
},
|
||||
});
|
||||
|
||||
_console.Profile.Width = 80;
|
||||
_console.Profile.Height = 24;
|
||||
_console.Profile.Capabilities.Ansi = true;
|
||||
_console.Profile.Capabilities.Unicode = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
_writer.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Clear(bool home)
|
||||
{
|
||||
_console.Clear(home);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Write(IRenderable renderable)
|
||||
{
|
||||
if (EmitAnsiSequences)
|
||||
{
|
||||
_console.Write(renderable);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var segment in renderable.GetSegments(this))
|
||||
{
|
||||
if (segment.IsControlCode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Profile.Out.Writer.Write(segment.Text);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetCursor(IAnsiConsoleCursor? cursor)
|
||||
{
|
||||
_cursor = cursor;
|
||||
Profile.Out.Writer.Write(segment.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetCursor(IAnsiConsoleCursor? cursor)
|
||||
{
|
||||
_cursor = cursor;
|
||||
}
|
||||
}
|
||||
|
@ -1,67 +1,66 @@
|
||||
namespace Spectre.Console.Testing
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
/// <summary>
|
||||
/// Contains extensions for <see cref="TestConsole"/>.
|
||||
/// </summary>
|
||||
public static class TestConsoleExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains extensions for <see cref="TestConsole"/>.
|
||||
/// Sets the console's color system.
|
||||
/// </summary>
|
||||
public static class TestConsoleExtensions
|
||||
/// <param name="console">The console.</param>
|
||||
/// <param name="colors">The color system to use.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TestConsole Colors(this TestConsole console, ColorSystem colors)
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the console's color system.
|
||||
/// </summary>
|
||||
/// <param name="console">The console.</param>
|
||||
/// <param name="colors">The color system to use.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TestConsole Colors(this TestConsole console, ColorSystem colors)
|
||||
{
|
||||
console.Profile.Capabilities.ColorSystem = colors;
|
||||
return console;
|
||||
}
|
||||
console.Profile.Capabilities.ColorSystem = colors;
|
||||
return console;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether or not ANSI is supported.
|
||||
/// </summary>
|
||||
/// <param name="console">The console.</param>
|
||||
/// <param name="enable">Whether or not VT/ANSI control codes are supported.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TestConsole SupportsAnsi(this TestConsole console, bool enable)
|
||||
{
|
||||
console.Profile.Capabilities.Ansi = enable;
|
||||
return console;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets whether or not ANSI is supported.
|
||||
/// </summary>
|
||||
/// <param name="console">The console.</param>
|
||||
/// <param name="enable">Whether or not VT/ANSI control codes are supported.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TestConsole SupportsAnsi(this TestConsole console, bool enable)
|
||||
{
|
||||
console.Profile.Capabilities.Ansi = enable;
|
||||
return console;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes the console interactive.
|
||||
/// </summary>
|
||||
/// <param name="console">The console.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TestConsole Interactive(this TestConsole console)
|
||||
{
|
||||
console.Profile.Capabilities.Interactive = true;
|
||||
return console;
|
||||
}
|
||||
/// <summary>
|
||||
/// Makes the console interactive.
|
||||
/// </summary>
|
||||
/// <param name="console">The console.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TestConsole Interactive(this TestConsole console)
|
||||
{
|
||||
console.Profile.Capabilities.Interactive = true;
|
||||
return console;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the console width.
|
||||
/// </summary>
|
||||
/// <param name="console">The console.</param>
|
||||
/// <param name="width">The console width.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TestConsole Width(this TestConsole console, int width)
|
||||
{
|
||||
console.Profile.Width = width;
|
||||
return console;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the console width.
|
||||
/// </summary>
|
||||
/// <param name="console">The console.</param>
|
||||
/// <param name="width">The console width.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TestConsole Width(this TestConsole console, int width)
|
||||
{
|
||||
console.Profile.Width = width;
|
||||
return console;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turns on emitting of VT/ANSI sequences.
|
||||
/// </summary>
|
||||
/// <param name="console">The console.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TestConsole EmitAnsiSequences(this TestConsole console)
|
||||
{
|
||||
console.SetCursor(null);
|
||||
console.EmitAnsiSequences = true;
|
||||
return console;
|
||||
}
|
||||
/// <summary>
|
||||
/// Turns on emitting of VT/ANSI sequences.
|
||||
/// </summary>
|
||||
/// <param name="console">The console.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TestConsole EmitAnsiSequences(this TestConsole console)
|
||||
{
|
||||
console.SetCursor(null);
|
||||
console.EmitAnsiSequences = true;
|
||||
return console;
|
||||
}
|
||||
}
|
||||
|
@ -3,90 +3,89 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spectre.Console.Testing
|
||||
namespace Spectre.Console.Testing;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a testable console input mechanism.
|
||||
/// </summary>
|
||||
public sealed class TestConsoleInput : IAnsiConsoleInput
|
||||
{
|
||||
private readonly Queue<ConsoleKeyInfo> _input;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a testable console input mechanism.
|
||||
/// Initializes a new instance of the <see cref="TestConsoleInput"/> class.
|
||||
/// </summary>
|
||||
public sealed class TestConsoleInput : IAnsiConsoleInput
|
||||
public TestConsoleInput()
|
||||
{
|
||||
private readonly Queue<ConsoleKeyInfo> _input;
|
||||
_input = new Queue<ConsoleKeyInfo>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestConsoleInput"/> class.
|
||||
/// </summary>
|
||||
public TestConsoleInput()
|
||||
/// <summary>
|
||||
/// Pushes the specified text to the input queue.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string.</param>
|
||||
public void PushText(string input)
|
||||
{
|
||||
if (input is null)
|
||||
{
|
||||
_input = new Queue<ConsoleKeyInfo>();
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes the specified text to the input queue.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string.</param>
|
||||
public void PushText(string input)
|
||||
foreach (var character in input)
|
||||
{
|
||||
if (input is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
}
|
||||
|
||||
foreach (var character in input)
|
||||
{
|
||||
PushCharacter(character);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes the specified text followed by 'Enter' to the input queue.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
public void PushTextWithEnter(string input)
|
||||
{
|
||||
PushText(input);
|
||||
PushKey(ConsoleKey.Enter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes the specified character to the input queue.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
public void PushCharacter(char input)
|
||||
{
|
||||
var control = char.IsUpper(input);
|
||||
_input.Enqueue(new ConsoleKeyInfo(input, (ConsoleKey)input, false, false, control));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes the specified key to the input queue.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
public void PushKey(ConsoleKey input)
|
||||
{
|
||||
_input.Enqueue(new ConsoleKeyInfo((char)input, input, false, false, false));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsKeyAvailable()
|
||||
{
|
||||
return _input.Count > 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConsoleKeyInfo? ReadKey(bool intercept)
|
||||
{
|
||||
if (_input.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("No input available.");
|
||||
}
|
||||
|
||||
return _input.Dequeue();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<ConsoleKeyInfo?> ReadKeyAsync(bool intercept, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(ReadKey(intercept));
|
||||
PushCharacter(character);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes the specified text followed by 'Enter' to the input queue.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
public void PushTextWithEnter(string input)
|
||||
{
|
||||
PushText(input);
|
||||
PushKey(ConsoleKey.Enter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes the specified character to the input queue.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
public void PushCharacter(char input)
|
||||
{
|
||||
var control = char.IsUpper(input);
|
||||
_input.Enqueue(new ConsoleKeyInfo(input, (ConsoleKey)input, false, false, control));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes the specified key to the input queue.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
public void PushKey(ConsoleKey input)
|
||||
{
|
||||
_input.Enqueue(new ConsoleKeyInfo((char)input, input, false, false, false));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsKeyAvailable()
|
||||
{
|
||||
return _input.Count > 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConsoleKeyInfo? ReadKey(bool intercept)
|
||||
{
|
||||
if (_input.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("No input available.");
|
||||
}
|
||||
|
||||
return _input.Dequeue();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<ConsoleKeyInfo?> ReadKeyAsync(bool intercept, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(ReadKey(intercept));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user