Add global usings (#668)

* Use global usings

* Fix namespace declarations for test projects
This commit is contained in:
Patrik Svensson
2021-12-23 16:50:31 +01:00
committed by GitHub
parent eb6a9d8d04
commit 52c1d9122b
514 changed files with 10659 additions and 12441 deletions

View File

@ -1,35 +1,107 @@
root = false
[*.cs]
[*.cs]
# Default severity for analyzer diagnostics with category 'StyleCop.CSharp.DocumentationRules'
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = none
# CA1707: Identifiers should not contain underscores
dotnet_diagnostic.CA1707.severity = none
# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = warning
# SA1600: Elements should be documented
dotnet_diagnostic.SA1600.severity = none
# SA1101: Prefix local calls with this
dotnet_diagnostic.SA1101.severity = none
# SA1601: Partial elements should be documented
dotnet_diagnostic.SA1601.severity = none
# SA1633: File should have header
dotnet_diagnostic.SA1633.severity = none
# SA1200: Using directives should be placed correctly
dotnet_diagnostic.SA1200.severity = none
# SA1201: Elements should appear in the correct order
dotnet_diagnostic.SA1201.severity = none
# SA1202: Public members should come before private members
dotnet_diagnostic.SA1202.severity = none
# SA1309: Field names should not begin with underscore
dotnet_diagnostic.SA1309.severity = none
# SA1404: Code analysis suppressions should have justification
dotnet_diagnostic.SA1404.severity = none
# SA1516: Elements should be separated by a blank line
dotnet_diagnostic.SA1516.severity = none
# CA1303: Do not pass literals as localized parameters
dotnet_diagnostic.CA1303.severity = none
# CSA1204: Static members should appear before non-static members
dotnet_diagnostic.SA1204.severity = none
# IDE0052: Remove unread private members
dotnet_diagnostic.IDE0052.severity = warning
# IDE0063: Use simple 'using' statement
csharp_prefer_simple_using_statement = false:suggestion
# IDE0018: Variable declaration can be inlined
dotnet_diagnostic.IDE0018.severity = warning
# SA1625: Element documenation should not be copied and pasted
dotnet_diagnostic.SA1625.severity = none
# IDE0005: Using directive is unnecessary
dotnet_diagnostic.IDE0005.severity = warning
# SA1117: Parameters should be on same line or separate lines
dotnet_diagnostic.SA1117.severity = none
# SA1404: Code analysis suppression should have justification
dotnet_diagnostic.SA1404.severity = none
# SA1101: Prefix local calls with this
dotnet_diagnostic.SA1101.severity = none
# SA1633: File should have header
dotnet_diagnostic.SA1633.severity = none
# SA1649: File name should match first type name
dotnet_diagnostic.SA1649.severity = none
# SA1402: File may only contain a single type
dotnet_diagnostic.SA1402.severity = none
# CA1814: Prefer jagged arrays over multidimensional
dotnet_diagnostic.CA1814.severity = none
# RCS1194: Implement exception constructors.
dotnet_diagnostic.RCS1194.severity = none
# CA1032: Implement standard exception constructors
dotnet_diagnostic.CA1032.severity = none
# CA1826: Do not use Enumerable methods on indexable collections. Instead use the collection directly
dotnet_diagnostic.CA1826.severity = none
# RCS1079: Throwing of new NotImplementedException.
dotnet_diagnostic.RCS1079.severity = warning
# RCS1057: Add empty line between declarations.
dotnet_diagnostic.RCS1057.severity = none
# RCS1057: Validate arguments correctly
dotnet_diagnostic.RCS1227.severity = none
# IDE0004: Remove Unnecessary Cast
dotnet_diagnostic.IDE0004.severity = warning
# CA1810: Initialize reference type static fields inline
dotnet_diagnostic.CA1810.severity = none
# IDE0044: Add readonly modifier
dotnet_diagnostic.IDE0044.severity = warning
# RCS1047: Non-asynchronous method name should not end with 'Async'.
dotnet_diagnostic.RCS1047.severity = none
# RCS1090: Call 'ConfigureAwait(false)'.
dotnet_diagnostic.RCS1090.severity = warning
# CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = none
# SA1210: Using directives should be ordered alphabetically by namespace
dotnet_diagnostic.SA1210.severity = none
# CA1034: Nested types should not be visible
dotnet_diagnostic.CA1034.severity = none
# CA2000: Dispose objects before losing scope
dotnet_diagnostic.CA2000.severity = none
# SA1118: Parameter should not span multiple lines
dotnet_diagnostic.SA1118.severity = none
# CA1031: Do not catch general exception types
dotnet_diagnostic.CA1031.severity = none
dotnet_diagnostic.CS1591.severity = none

View File

@ -0,0 +1,16 @@
<Project>
<PropertyGroup Label="Settings">
<LangVersion>10</LangVersion>
<IsPackable>false</IsPackable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.312">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Roslynator.Analyzers" Version="3.3.0">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,12 @@
namespace Spectre.Console.Analyzer.Tests;
internal static class CodeAnalyzerHelper
{
internal static ReferenceAssemblies CurrentSpectre { get; }
static CodeAnalyzerHelper()
{
CurrentSpectre = ReferenceAssemblies.Net.Net50.AddAssemblies(
ImmutableArray.Create(typeof(AnsiConsole).Assembly.Location.Replace(".dll", string.Empty)));
}
}

View File

@ -1,55 +1,44 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.VisualStudio.Composition;
using Spectre.Console.Analyzer.FixProviders;
namespace Spectre.Console.Analyzer.Tests;
namespace Spectre.Console.Analyzer.Tests
internal static class CodeFixProviderDiscovery
{
internal static class CodeFixProviderDiscovery
private static readonly Lazy<IExportProviderFactory> _exportProviderFactory;
static CodeFixProviderDiscovery()
{
private static readonly Lazy<IExportProviderFactory> _exportProviderFactory;
static CodeFixProviderDiscovery()
{
_exportProviderFactory = new Lazy<IExportProviderFactory>(
() =>
{
var discovery = new AttributedPartDiscovery(Resolver.DefaultInstance, isNonPublicSupported: true);
var parts = Task.Run(() => discovery.CreatePartsAsync(typeof(SystemConsoleToAnsiConsoleFix).Assembly)).GetAwaiter().GetResult();
var catalog = ComposableCatalog.Create(Resolver.DefaultInstance).AddParts(parts);
var configuration = CompositionConfiguration.Create(catalog);
var runtimeComposition = RuntimeComposition.CreateRuntimeComposition(configuration);
return runtimeComposition.CreateExportProviderFactory();
},
LazyThreadSafetyMode.ExecutionAndPublication);
}
public static IEnumerable<CodeFixProvider> GetCodeFixProviders(string language)
{
var exportProvider = _exportProviderFactory.Value.CreateExportProvider();
var exports = exportProvider.GetExports<CodeFixProvider, LanguageMetadata>();
return exports.Where(export => export.Metadata.Languages.Contains(language)).Select(export => export.Value);
}
private class LanguageMetadata
{
public LanguageMetadata(IDictionary<string, object> data)
_exportProviderFactory = new Lazy<IExportProviderFactory>(
() =>
{
if (!data.TryGetValue(nameof(ExportCodeFixProviderAttribute.Languages), out var languages))
{
languages = Array.Empty<string>();
}
var discovery = new AttributedPartDiscovery(Resolver.DefaultInstance, isNonPublicSupported: true);
var parts = Task.Run(() => discovery.CreatePartsAsync(typeof(SystemConsoleToAnsiConsoleFix).Assembly)).GetAwaiter().GetResult();
var catalog = ComposableCatalog.Create(Resolver.DefaultInstance).AddParts(parts);
Languages = ((string[])languages).ToImmutableArray();
var configuration = CompositionConfiguration.Create(catalog);
var runtimeComposition = RuntimeComposition.CreateRuntimeComposition(configuration);
return runtimeComposition.CreateExportProviderFactory();
},
LazyThreadSafetyMode.ExecutionAndPublication);
}
public static IEnumerable<CodeFixProvider> GetCodeFixProviders(string language)
{
var exportProvider = _exportProviderFactory.Value.CreateExportProvider();
var exports = exportProvider.GetExports<CodeFixProvider, LanguageMetadata>();
return exports.Where(export => export.Metadata.Languages.Contains(language)).Select(export => export.Value);
}
private class LanguageMetadata
{
public LanguageMetadata(IDictionary<string, object> data)
{
if (!data.TryGetValue(nameof(ExportCodeFixProviderAttribute.Languages), out var languages))
{
languages = Array.Empty<string>();
}
public ImmutableArray<string> Languages { get; }
Languages = ((string[])languages).ToImmutableArray();
}
public ImmutableArray<string> Languages { get; }
}
}
}

View File

@ -0,0 +1,15 @@
global using System;
global using System.Collections.Generic;
global using System.Collections.Immutable;
global using System.Linq;
global using System.Threading;
global using System.Threading.Tasks;
global using Microsoft.CodeAnalysis;
global using Microsoft.CodeAnalysis.CodeFixes;
global using Microsoft.CodeAnalysis.CSharp.Testing;
global using Microsoft.CodeAnalysis.Diagnostics;
global using Microsoft.CodeAnalysis.Testing;
global using Microsoft.CodeAnalysis.Testing.Verifiers;
global using Microsoft.VisualStudio.Composition;
global using Spectre.Console.Analyzer.FixProviders;
global using Xunit;

View File

@ -5,6 +5,10 @@
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\src\stylecop.json" Link="Properties/stylecop.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.0" />

View File

@ -1,86 +1,63 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
namespace Spectre.Console.Analyzer.Tests;
namespace Spectre.Console.Analyzer.Tests
public static class SpectreAnalyzerVerifier<TAnalyzer>
where TAnalyzer : DiagnosticAnalyzer, new()
{
public static class SpectreAnalyzerVerifier<TAnalyzer>
where TAnalyzer : DiagnosticAnalyzer, new()
public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource)
=> VerifyCodeFixAsync(source, new[] { expected }, fixedSource);
private static Task VerifyCodeFixAsync(string source, IEnumerable<DiagnosticResult> expected, string fixedSource)
{
public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource)
=> VerifyCodeFixAsync(source, new[] { expected }, fixedSource);
private static Task VerifyCodeFixAsync(string source, IEnumerable<DiagnosticResult> expected, string fixedSource)
// Roslyn fixers always use \r\n for newlines, regardless of OS environment settings, so we normalize
// the source as it typically comes from multi-line strings with varying newlines.
if (Environment.NewLine != "\r\n")
{
// Roslyn fixers always use \r\n for newlines, regardless of OS environment settings, so we normalize
// the source as it typically comes from multi-line strings with varying newlines.
if (Environment.NewLine != "\r\n")
{
source = source.Replace(Environment.NewLine, "\r\n");
fixedSource = fixedSource.Replace(Environment.NewLine, "\r\n");
}
var test = new Test
{
TestCode = source,
FixedCode = fixedSource,
};
test.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
source = source.Replace(Environment.NewLine, "\r\n");
fixedSource = fixedSource.Replace(Environment.NewLine, "\r\n");
}
public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
var test = new Test
{
var test = new Test
{
TestCode = source,
CompilerDiagnostics = CompilerDiagnostics.All,
};
TestCode = source,
FixedCode = fixedSource,
};
test.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
test.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
{
var test = new Test
{
TestCode = source,
CompilerDiagnostics = CompilerDiagnostics.All,
};
test.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
// Code fix tests support both analyzer and code fix testing. This test class is derived from the code fix test
// to avoid the need to maintain duplicate copies of the customization work.
private class Test : CSharpCodeFixTest<TAnalyzer, EmptyCodeFixProvider, XUnitVerifier>
{
public Test()
{
ReferenceAssemblies = CodeAnalyzerHelper.CurrentSpectre;
TestBehaviors |= TestBehaviors.SkipGeneratedCodeCheck;
}
// Code fix tests support both analyzer and code fix testing. This test class is derived from the code fix test
// to avoid the need to maintain duplicate copies of the customization work.
private class Test : CSharpCodeFixTest<TAnalyzer, EmptyCodeFixProvider, XUnitVerifier>
protected override IEnumerable<CodeFixProvider> GetCodeFixProviders()
{
public Test()
var analyzer = new TAnalyzer();
foreach (var provider in CodeFixProviderDiscovery.GetCodeFixProviders(Language))
{
ReferenceAssemblies = CodeAnalyzerHelper.CurrentSpectre;
TestBehaviors |= TestBehaviors.SkipGeneratedCodeCheck;
}
protected override IEnumerable<CodeFixProvider> GetCodeFixProviders()
{
var analyzer = new TAnalyzer();
foreach (var provider in CodeFixProviderDiscovery.GetCodeFixProviders(Language))
if (analyzer.SupportedDiagnostics.Any(diagnostic => provider.FixableDiagnosticIds.Contains(diagnostic.Id)))
{
if (analyzer.SupportedDiagnostics.Any(diagnostic => provider.FixableDiagnosticIds.Contains(diagnostic.Id)))
{
yield return provider;
}
yield return provider;
}
}
}
}
internal static class CodeAnalyzerHelper
{
internal static ReferenceAssemblies CurrentSpectre { get; }
static CodeAnalyzerHelper()
{
CurrentSpectre = ReferenceAssemblies.Net.Net50.AddAssemblies(
ImmutableArray.Create(typeof(AnsiConsole).Assembly.Location.Replace(".dll", string.Empty)));
}
}
}
}

View File

@ -1,19 +1,15 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
namespace Spectre.Console.Analyzer.Tests.Unit.Analyzers;
namespace Spectre.Console.Analyzer.Tests.Unit.Analyzers
public class NoCurrentLiveRenderablesTests
{
public class NoCurrentLiveRenderablesTests
{
private static readonly DiagnosticResult _expectedDiagnostics = new(
Descriptors.S1020_AvoidConcurrentCallsToMultipleLiveRenderables.Id,
DiagnosticSeverity.Warning);
private static readonly DiagnosticResult _expectedDiagnostics = new(
Descriptors.S1020_AvoidConcurrentCallsToMultipleLiveRenderables.Id,
DiagnosticSeverity.Warning);
[Fact]
public async void Status_call_within_live_call_warns()
{
const string Source = @"
[Fact]
public async void Status_call_within_live_call_warns()
{
const string Source = @"
using Spectre.Console;
class TestClass
@ -27,15 +23,15 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<NoConcurrentLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(10, 13))
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<NoConcurrentLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(10, 13))
.ConfigureAwait(false);
}
[Fact]
public async void Status_call_within_live_call_warns_with_instance()
{
const string Source = @"
[Fact]
public async void Status_call_within_live_call_warns_with_instance()
{
const string Source = @"
using Spectre.Console;
class Child
@ -51,15 +47,15 @@ class Child
}
}";
await SpectreAnalyzerVerifier<NoConcurrentLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(12, 13))
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<NoConcurrentLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(12, 13))
.ConfigureAwait(false);
}
[Fact]
public async void Calling_start_on_non_live_renderable_has_no_warning()
{
const string Source = @"
[Fact]
public async void Calling_start_on_non_live_renderable_has_no_warning()
{
const string Source = @"
using Spectre.Console;
class Program
@ -72,9 +68,8 @@ class Program
static void Start() => AnsiConsole.WriteLine(""Starting..."");
}";
await SpectreAnalyzerVerifier<NoConcurrentLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<NoConcurrentLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
}
}

View File

@ -1,20 +1,15 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
namespace Spectre.Console.Analyzer.Tests.Unit.Analyzers;
namespace Spectre.Console.Analyzer.Tests.Unit.Analyzers
public class NoPromptsDuringLiveRenderablesTests
{
public class NoPromptsDuringLiveRenderablesTests
{
private static readonly DiagnosticResult _expectedDiagnostics = new(
Descriptors.S1021_AvoidPromptCallsDuringLiveRenderables.Id,
DiagnosticSeverity.Warning);
private static readonly DiagnosticResult _expectedDiagnostics = new(
Descriptors.S1021_AvoidPromptCallsDuringLiveRenderables.Id,
DiagnosticSeverity.Warning);
[Fact]
public async Task Prompt_out_of_progress_does_not_warn()
{
const string Source = @"
[Fact]
public async Task Prompt_out_of_progress_does_not_warn()
{
const string Source = @"
using Spectre.Console;
class TestClass
@ -25,15 +20,15 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<NoPromptsDuringLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<NoPromptsDuringLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
[Fact]
public async Task Instance_variables_warn()
{
const string Source = @"
[Fact]
public async Task Instance_variables_warn()
{
const string Source = @"
using Spectre.Console;
class TestClass
@ -49,15 +44,15 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<NoPromptsDuringLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(12, 26))
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<NoPromptsDuringLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(12, 26))
.ConfigureAwait(false);
}
[Fact]
public async Task Prompt_in_progress_warns()
{
const string Source = @"
[Fact]
public async Task Prompt_in_progress_warns()
{
const string Source = @"
using Spectre.Console;
class TestClass
@ -71,15 +66,15 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<NoPromptsDuringLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(10, 13))
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<NoPromptsDuringLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(10, 13))
.ConfigureAwait(false);
}
[Fact]
public async Task Can_call_other_methods_from_within_renderables()
{
const string Source = @"
[Fact]
public async Task Can_call_other_methods_from_within_renderables()
{
const string Source = @"
using Spectre.Console;
class Program
@ -96,9 +91,8 @@ class Program
static string Confirm() => string.Empty;
}";
await SpectreAnalyzerVerifier<NoPromptsDuringLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<NoPromptsDuringLiveRenderablesAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
}
}

View File

@ -1,19 +1,15 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
namespace Spectre.Console.Analyzer.Tests.Unit.Analyzers;
namespace Spectre.Console.Analyzer.Tests.Unit.Analyzers
public class FavorInstanceAnsiConsoleOverStaticAnalyzerTests
{
public class FavorInstanceAnsiConsoleOverStaticAnalyzerTests
{
private static readonly DiagnosticResult _expectedDiagnostics = new(
Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic.Id,
DiagnosticSeverity.Info);
private static readonly DiagnosticResult _expectedDiagnostics = new(
Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic.Id,
DiagnosticSeverity.Info);
[Fact]
public async void Should_only_warn_within_methods()
{
const string Source = @"
[Fact]
public async void Should_only_warn_within_methods()
{
const string Source = @"
using Spectre.Console;
internal sealed class Foo
@ -27,15 +23,15 @@ internal sealed class Foo
}
";
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
[Fact]
public async void Instance_console_has_no_warnings()
{
const string Source = @"
[Fact]
public async void Instance_console_has_no_warnings()
{
const string Source = @"
using Spectre.Console;
class TestClass
@ -48,15 +44,15 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
[Fact]
public async void Static_console_with_no_instance_variables_has_no_warnings()
{
const string Source = @"
[Fact]
public async void Static_console_with_no_instance_variables_has_no_warnings()
{
const string Source = @"
using Spectre.Console;
class TestClass
@ -67,15 +63,15 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
[Fact]
public async void Console_Write_Has_Warning()
{
const string Source = @"
[Fact]
public async void Console_Write_Has_Warning()
{
const string Source = @"
using Spectre.Console;
class TestClass
@ -89,9 +85,8 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(11, 9))
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(11, 9))
.ConfigureAwait(false);
}
}
}

View File

@ -1,19 +1,15 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
namespace Spectre.Console.Analyzer.Tests.Unit.Analyzers;
namespace Spectre.Console.Analyzer.Tests.Unit.Analyzers
public class UseSpectreInsteadOfSystemConsoleAnalyzerTests
{
public class UseSpectreInsteadOfSystemConsoleAnalyzerTests
{
private static readonly DiagnosticResult _expectedDiagnostics = new(
Descriptors.S1000_UseAnsiConsoleOverSystemConsole.Id,
DiagnosticSeverity.Warning);
private static readonly DiagnosticResult _expectedDiagnostics = new(
Descriptors.S1000_UseAnsiConsoleOverSystemConsole.Id,
DiagnosticSeverity.Warning);
[Fact]
public async void Non_configured_SystemConsole_methods_report_no_warnings()
{
const string Source = @"
[Fact]
public async void Non_configured_SystemConsole_methods_report_no_warnings()
{
const string Source = @"
using System;
class TestClass {
@ -23,15 +19,15 @@ class TestClass {
}
}";
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyAnalyzerAsync(Source)
.ConfigureAwait(false);
}
[Fact]
public async void Console_Write_Has_Warning()
{
const string Source = @"
[Fact]
public async void Console_Write_Has_Warning()
{
const string Source = @"
using System;
class TestClass {
@ -41,15 +37,15 @@ class TestClass {
}
}";
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(7, 9))
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(7, 9))
.ConfigureAwait(false);
}
[Fact]
public async void Console_WriteLine_Has_Warning()
{
const string Source = @"
[Fact]
public async void Console_WriteLine_Has_Warning()
{
const string Source = @"
using System;
class TestClass
@ -59,9 +55,8 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(7, 9))
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(7, 9))
.ConfigureAwait(false);
}
}
}

View File

@ -1,20 +1,15 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
namespace Spectre.Console.Analyzer.Tests.Unit.Fixes;
namespace Spectre.Console.Analyzer.Tests.Unit.Fixes
public class UseInstanceOfStaticAnsiConsoleTests
{
public class UseInstanceOfStaticAnsiConsoleTests
{
private static readonly DiagnosticResult _expectedDiagnostic = new(
Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic.Id,
DiagnosticSeverity.Info);
private static readonly DiagnosticResult _expectedDiagnostic = new(
Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic.Id,
DiagnosticSeverity.Info);
[Fact]
public async Task Static_call_replaced_with_field_call()
{
const string Source = @"
[Fact]
public async Task Static_call_replaced_with_field_call()
{
const string Source = @"
using Spectre.Console;
class TestClass
@ -28,7 +23,7 @@ class TestClass
}
}";
const string FixedSource = @"
const string FixedSource = @"
using Spectre.Console;
class TestClass
@ -42,15 +37,15 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource)
.ConfigureAwait(false);
}
[Fact]
public async Task Static_call_replaced_with_field_call_Should_Preserve_Trivia()
{
const string Source = @"
[Fact]
public async Task Static_call_replaced_with_field_call_Should_Preserve_Trivia()
{
const string Source = @"
using Spectre.Console;
class TestClass
@ -66,7 +61,7 @@ class TestClass
}
}";
const string FixedSource = @"
const string FixedSource = @"
using Spectre.Console;
class TestClass
@ -82,15 +77,15 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(12, 9), FixedSource)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(12, 9), FixedSource)
.ConfigureAwait(false);
}
[Fact]
public async Task Static_call_replaced_with_parameter_call()
{
const string Source = @"
[Fact]
public async Task Static_call_replaced_with_parameter_call()
{
const string Source = @"
using Spectre.Console;
class TestClass
@ -101,7 +96,7 @@ class TestClass
}
}";
const string FixedSource = @"
const string FixedSource = @"
using Spectre.Console;
class TestClass
@ -112,15 +107,15 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(8, 9), FixedSource)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(8, 9), FixedSource)
.ConfigureAwait(false);
}
[Fact]
public async Task Static_call_replaced_with_static_field_if_valid()
{
const string Source = @"
[Fact]
public async Task Static_call_replaced_with_static_field_if_valid()
{
const string Source = @"
using Spectre.Console;
class TestClass
@ -134,7 +129,7 @@ class TestClass
}
}";
const string FixedSource = @"
const string FixedSource = @"
using Spectre.Console;
class TestClass
@ -148,9 +143,8 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource)
.ConfigureAwait(false);
}
}
}

View File

@ -1,20 +1,15 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
namespace Spectre.Console.Analyzer.Tests.Unit.Fixes;
namespace Spectre.Console.Analyzer.Tests.Unit.Fixes
public class UseSpectreInsteadOfSystemConsoleFixTests
{
public class UseSpectreInsteadOfSystemConsoleFixTests
{
private static readonly DiagnosticResult _expectedDiagnostic = new(
Descriptors.S1000_UseAnsiConsoleOverSystemConsole.Id,
DiagnosticSeverity.Warning);
private static readonly DiagnosticResult _expectedDiagnostic = new(
Descriptors.S1000_UseAnsiConsoleOverSystemConsole.Id,
DiagnosticSeverity.Warning);
[Fact]
public async Task SystemConsole_replaced_with_AnsiConsole()
{
const string Source = @"
[Fact]
public async Task SystemConsole_replaced_with_AnsiConsole()
{
const string Source = @"
using System;
class TestClass
@ -25,7 +20,7 @@ class TestClass
}
}";
const string FixedSource = @"
const string FixedSource = @"
using System;
using Spectre.Console;
@ -37,15 +32,15 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(8, 9), FixedSource)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(8, 9), FixedSource)
.ConfigureAwait(false);
}
[Fact]
public async Task SystemConsole_replaced_with_imported_AnsiConsole()
{
const string Source = @"
[Fact]
public async Task SystemConsole_replaced_with_imported_AnsiConsole()
{
const string Source = @"
using System;
class TestClass
@ -56,7 +51,7 @@ class TestClass
}
}";
const string FixedSource = @"
const string FixedSource = @"
using System;
using Spectre.Console;
@ -68,15 +63,15 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(8, 9), FixedSource)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(8, 9), FixedSource)
.ConfigureAwait(false);
}
[Fact]
public async Task SystemConsole_replaced_with_field_AnsiConsole()
{
const string Source = @"
[Fact]
public async Task SystemConsole_replaced_with_field_AnsiConsole()
{
const string Source = @"
using System;
using Spectre.Console;
@ -90,7 +85,7 @@ class TestClass
}
}";
const string FixedSource = @"
const string FixedSource = @"
using System;
using Spectre.Console;
@ -104,15 +99,15 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource)
.ConfigureAwait(false);
}
[Fact]
public async Task SystemConsole_replaced_with_static_field_AnsiConsole()
{
const string Source = @"
[Fact]
public async Task SystemConsole_replaced_with_static_field_AnsiConsole()
{
const string Source = @"
using System;
using Spectre.Console;
@ -126,7 +121,7 @@ class TestClass
}
}";
const string FixedSource = @"
const string FixedSource = @"
using System;
using Spectre.Console;
@ -140,9 +135,8 @@ class TestClass
}
}";
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource)
.ConfigureAwait(false);
}
await SpectreAnalyzerVerifier<UseSpectreInsteadOfSystemConsoleAnalyzer>
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource)
.ConfigureAwait(false);
}
}
}

View File

@ -1,21 +1,18 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests;
namespace Spectre.Console.Tests
public static class Constants
{
public static class Constants
{
public static string[] VersionCommand { get; } =
new[]
{
public static string[] VersionCommand { get; } =
new[]
{
CliConstants.Commands.Branch,
CliConstants.Commands.Version,
};
};
public static string[] XmlDocCommand { get; } =
new[]
{
public static string[] XmlDocCommand { get; } =
new[]
{
CliConstants.Commands.Branch,
CliConstants.Commands.XmlDoc,
};
}
}
};
}

View File

@ -1,49 +1,45 @@
using System;
using System.Linq;
using Spectre.Console.Cli;
using SystemConsole = System.Console;
namespace Spectre.Console.Tests.Data
namespace Spectre.Console.Tests.Data;
public abstract class AnimalCommand<TSettings> : Command<TSettings>
where TSettings : CommandSettings
{
public abstract class AnimalCommand<TSettings> : Command<TSettings>
where TSettings : CommandSettings
protected void DumpSettings(CommandContext context, TSettings settings)
{
protected void DumpSettings(CommandContext context, TSettings settings)
if (context == null)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (settings == null)
{
throw new ArgumentNullException(nameof(settings));
}
var properties = settings.GetType().GetProperties();
foreach (var group in properties.GroupBy(x => x.DeclaringType).Reverse())
{
SystemConsole.WriteLine();
SystemConsole.ForegroundColor = ConsoleColor.Yellow;
SystemConsole.WriteLine(group.Key.FullName);
SystemConsole.ResetColor();
foreach (var property in group)
{
SystemConsole.WriteLine($" {property.Name} = {property.GetValue(settings)}");
}
}
if (context.Remaining.Raw.Count > 0)
{
SystemConsole.WriteLine();
SystemConsole.ForegroundColor = ConsoleColor.Yellow;
SystemConsole.WriteLine("Remaining:");
SystemConsole.ResetColor();
SystemConsole.WriteLine(string.Join(", ", context.Remaining));
}
SystemConsole.WriteLine();
throw new ArgumentNullException(nameof(context));
}
if (settings == null)
{
throw new ArgumentNullException(nameof(settings));
}
var properties = settings.GetType().GetProperties();
foreach (var group in properties.GroupBy(x => x.DeclaringType).Reverse())
{
SystemConsole.WriteLine();
SystemConsole.ForegroundColor = ConsoleColor.Yellow;
SystemConsole.WriteLine(group.Key.FullName);
SystemConsole.ResetColor();
foreach (var property in group)
{
SystemConsole.WriteLine($" {property.Name} = {property.GetValue(settings)}");
}
}
if (context.Remaining.Raw.Count > 0)
{
SystemConsole.WriteLine();
SystemConsole.ForegroundColor = ConsoleColor.Yellow;
SystemConsole.WriteLine("Remaining:");
SystemConsole.ResetColor();
SystemConsole.WriteLine(string.Join(", ", context.Remaining));
}
SystemConsole.WriteLine();
}
}
}

View File

@ -1,13 +1,10 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public class CatCommand : AnimalCommand<CatSettings>
{
public class CatCommand : AnimalCommand<CatSettings>
public override int Execute(CommandContext context, CatSettings settings)
{
public override int Execute(CommandContext context, CatSettings settings)
{
DumpSettings(context, settings);
return 0;
}
DumpSettings(context, settings);
return 0;
}
}
}

View File

@ -1,36 +1,31 @@
using System.ComponentModel;
using System.Linq;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
[Description("The dog command.")]
public class DogCommand : AnimalCommand<DogSettings>
{
[Description("The dog command.")]
public class DogCommand : AnimalCommand<DogSettings>
public override ValidationResult Validate(CommandContext context, DogSettings settings)
{
public override ValidationResult Validate(CommandContext context, DogSettings settings)
if (context is null)
{
if (context is null)
{
throw new System.ArgumentNullException(nameof(context));
}
if (settings is null)
{
throw new System.ArgumentNullException(nameof(settings));
}
if (settings.Age > 100 && !context.Remaining.Raw.Contains("zombie"))
{
return ValidationResult.Error("Dog is too old...");
}
return base.Validate(context, settings);
throw new System.ArgumentNullException(nameof(context));
}
public override int Execute(CommandContext context, DogSettings settings)
if (settings is null)
{
DumpSettings(context, settings);
return 0;
throw new System.ArgumentNullException(nameof(settings));
}
if (settings.Age > 100 && !context.Remaining.Raw.Contains("zombie"))
{
return ValidationResult.Error("Dog is too old...");
}
return base.Validate(context, settings);
}
}
public override int Execute(CommandContext context, DogSettings settings)
{
DumpSettings(context, settings);
return 0;
}
}

View File

@ -1,39 +1,34 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class DumpRemainingCommand : Command<EmptyCommandSettings>
{
public sealed class DumpRemainingCommand : Command<EmptyCommandSettings>
private readonly IAnsiConsole _console;
public DumpRemainingCommand(IAnsiConsole console)
{
private readonly IAnsiConsole _console;
public DumpRemainingCommand(IAnsiConsole console)
{
_console = console;
}
public override int Execute([NotNull] CommandContext context, [NotNull] EmptyCommandSettings settings)
{
if (context.Remaining.Raw.Count > 0)
{
_console.WriteLine("# Raw");
foreach (var item in context.Remaining.Raw)
{
_console.WriteLine(item);
}
}
if (context.Remaining.Parsed.Count > 0)
{
_console.WriteLine("# Parsed");
foreach (var item in context.Remaining.Parsed)
{
_console.WriteLine(string.Format("{0}={1}", item.Key, string.Join(",", item.Select(x => x))));
}
}
return 0;
}
_console = console;
}
}
public override int Execute([NotNull] CommandContext context, [NotNull] EmptyCommandSettings settings)
{
if (context.Remaining.Raw.Count > 0)
{
_console.WriteLine("# Raw");
foreach (var item in context.Remaining.Raw)
{
_console.WriteLine(item);
}
}
if (context.Remaining.Parsed.Count > 0)
{
_console.WriteLine("# Parsed");
foreach (var item in context.Remaining.Parsed)
{
_console.WriteLine(string.Format("{0}={1}", item.Key, string.Join(",", item.Select(x => x))));
}
}
return 0;
}
}

View File

@ -1,12 +1,9 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class EmptyCommand : Command<EmptyCommandSettings>
{
public sealed class EmptyCommand : Command<EmptyCommandSettings>
public override int Execute(CommandContext context, EmptyCommandSettings settings)
{
public override int Execute(CommandContext context, EmptyCommandSettings settings)
{
return 0;
}
return 0;
}
}
}

View File

@ -1,13 +1,10 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class GenericCommand<TSettings> : Command<TSettings>
where TSettings : CommandSettings
{
public sealed class GenericCommand<TSettings> : Command<TSettings>
where TSettings : CommandSettings
public override int Execute(CommandContext context, TSettings settings)
{
public override int Execute(CommandContext context, TSettings settings)
{
return 0;
}
return 0;
}
}
}

View File

@ -1,14 +1,10 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
[Description("The giraffe command.")]
public sealed class GiraffeCommand : Command<GiraffeSettings>
{
[Description("The giraffe command.")]
public sealed class GiraffeCommand : Command<GiraffeSettings>
public override int Execute(CommandContext context, GiraffeSettings settings)
{
public override int Execute(CommandContext context, GiraffeSettings settings)
{
return 0;
}
return 0;
}
}
}

View File

@ -1,12 +1,9 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class HiddenOptionsCommand : Command<HiddenOptionSettings>
{
public sealed class HiddenOptionsCommand : Command<HiddenOptionSettings>
public override int Execute(CommandContext context, HiddenOptionSettings settings)
{
public override int Execute(CommandContext context, HiddenOptionSettings settings)
{
return 0;
}
return 0;
}
}
}

View File

@ -1,15 +1,11 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
[Description("The horse command.")]
public class HorseCommand : AnimalCommand<MammalSettings>
{
[Description("The horse command.")]
public class HorseCommand : AnimalCommand<MammalSettings>
public override int Execute(CommandContext context, MammalSettings settings)
{
public override int Execute(CommandContext context, MammalSettings settings)
{
DumpSettings(context, settings);
return 0;
}
DumpSettings(context, settings);
return 0;
}
}
}

View File

@ -1,12 +1,9 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class InvalidCommand : Command<InvalidSettings>
{
public sealed class InvalidCommand : Command<InvalidSettings>
public override int Execute(CommandContext context, InvalidSettings settings)
{
public override int Execute(CommandContext context, InvalidSettings settings)
{
return 0;
}
return 0;
}
}
}

View File

@ -1,14 +1,10 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
[Description("The lion command.")]
public class LionCommand : AnimalCommand<LionSettings>
{
[Description("The lion command.")]
public class LionCommand : AnimalCommand<LionSettings>
public override int Execute(CommandContext context, LionSettings settings)
{
public override int Execute(CommandContext context, LionSettings settings)
{
return 0;
}
return 0;
}
}
}

View File

@ -1,16 +1,12 @@
using System.Diagnostics.CodeAnalysis;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class NoDescriptionCommand : Command<EmptyCommandSettings>
{
public sealed class NoDescriptionCommand : Command<EmptyCommandSettings>
{
[CommandOption("-f|--foo <VALUE>")]
public int Foo { get; set; }
[CommandOption("-f|--foo <VALUE>")]
public int Foo { get; set; }
public override int Execute([NotNull] CommandContext context, [NotNull] EmptyCommandSettings settings)
{
return 0;
}
public override int Execute([NotNull] CommandContext context, [NotNull] EmptyCommandSettings settings)
{
return 0;
}
}
}

View File

@ -1,12 +1,9 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public class OptionVectorCommand : Command<OptionVectorSettings>
{
public class OptionVectorCommand : Command<OptionVectorSettings>
public override int Execute(CommandContext context, OptionVectorSettings settings)
{
public override int Execute(CommandContext context, OptionVectorSettings settings)
{
return 0;
}
return 0;
}
}
}

View File

@ -1,17 +1,13 @@
using System;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class ThrowingCommand : Command<ThrowingCommandSettings>
{
public sealed class ThrowingCommand : Command<ThrowingCommandSettings>
public override int Execute(CommandContext context, ThrowingCommandSettings settings)
{
public override int Execute(CommandContext context, ThrowingCommandSettings settings)
{
throw new InvalidOperationException("W00t?");
}
throw new InvalidOperationException("W00t?");
}
}
public sealed class ThrowingCommandSettings : CommandSettings
{
}
}
public sealed class ThrowingCommandSettings : CommandSettings
{
}

View File

@ -1,18 +1,14 @@
using System.ComponentModel;
using System.Globalization;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class CatAgilityConverter : TypeConverter
{
public sealed class CatAgilityConverter : TypeConverter
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
if (value is string stringValue)
{
if (value is string stringValue)
{
return stringValue.Length;
}
return base.ConvertFrom(context, culture, value);
return stringValue.Length;
}
return base.ConvertFrom(context, culture, value);
}
}
}

View File

@ -1,18 +1,14 @@
using System.ComponentModel;
using System.Globalization;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class StringToIntegerConverter : TypeConverter
{
public sealed class StringToIntegerConverter : TypeConverter
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
if (value is string stringValue)
{
if (value is string stringValue)
{
return int.Parse(stringValue, CultureInfo.InvariantCulture);
}
return base.ConvertFrom(context, culture, value);
return int.Parse(stringValue, CultureInfo.InvariantCulture);
}
return base.ConvertFrom(context, culture, value);
}
}
}

View File

@ -1,35 +1,32 @@
using System;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public static class TestExceptions
{
public static class TestExceptions
public static bool MethodThatThrows(int? number) => throw new InvalidOperationException("Throwing!");
public static bool GenericMethodThatThrows<T0, T1, TRet>(int? number) => throw new InvalidOperationException("Throwing!");
public static void ThrowWithInnerException()
{
public static bool MethodThatThrows(int? number) => throw new InvalidOperationException("Throwing!");
public static bool GenericMethodThatThrows<T0, T1, TRet>(int? number) => throw new InvalidOperationException("Throwing!");
public static void ThrowWithInnerException()
try
{
try
{
MethodThatThrows(null);
}
catch (Exception ex)
{
throw new InvalidOperationException("Something threw!", ex);
}
MethodThatThrows(null);
}
public static void ThrowWithGenericInnerException()
catch (Exception ex)
{
try
{
GenericMethodThatThrows<int, float, double>(null);
}
catch (Exception ex)
{
throw new InvalidOperationException("Something threw!", ex);
}
throw new InvalidOperationException("Something threw!", ex);
}
}
}
public static void ThrowWithGenericInnerException()
{
try
{
GenericMethodThatThrows<int, float, double>(null);
}
catch (Exception ex)
{
throw new InvalidOperationException("Something threw!", ex);
}
}
}

View File

@ -1,18 +1,14 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public abstract class AnimalSettings : CommandSettings
{
public abstract class AnimalSettings : CommandSettings
{
[CommandOption("-a|--alive|--not-dead")]
[Description("Indicates whether or not the animal is alive.")]
public bool IsAlive { get; set; }
[CommandOption("-a|--alive|--not-dead")]
[Description("Indicates whether or not the animal is alive.")]
public bool IsAlive { get; set; }
[CommandArgument(1, "[LEGS]")]
[Description("The number of legs.")]
[EvenNumberValidator("Animals must have an even number of legs.")]
[PositiveNumberValidator("Number of legs must be greater than 0.")]
public int Legs { get; set; }
}
}
[CommandArgument(1, "[LEGS]")]
[Description("The number of legs.")]
[EvenNumberValidator("Animals must have an even number of legs.")]
[PositiveNumberValidator("Number of legs must be greater than 0.")]
public int Legs { get; set; }
}

View File

@ -1,22 +1,19 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class ArgumentOrderSettings : CommandSettings
{
public sealed class ArgumentOrderSettings : CommandSettings
{
[CommandArgument(0, "[QUX]")]
public int Qux { get; set; }
[CommandArgument(0, "[QUX]")]
public int Qux { get; set; }
[CommandArgument(3, "<CORGI>")]
public int Corgi { get; set; }
[CommandArgument(3, "<CORGI>")]
public int Corgi { get; set; }
[CommandArgument(1, "<BAR>")]
public int Bar { get; set; }
[CommandArgument(1, "<BAR>")]
public int Bar { get; set; }
[CommandArgument(2, "<BAZ>")]
public int Baz { get; set; }
[CommandArgument(2, "<BAZ>")]
public int Baz { get; set; }
[CommandArgument(0, "<FOO>")]
public int Foo { get; set; }
}
}
[CommandArgument(0, "<FOO>")]
public int Foo { get; set; }
}

View File

@ -1,10 +1,7 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public class ArgumentVectorSettings : CommandSettings
{
public class ArgumentVectorSettings : CommandSettings
{
[CommandArgument(0, "<Foos>")]
public string[] Foo { get; set; }
}
}
[CommandArgument(0, "<Foos>")]
public string[] Foo { get; set; }
}

View File

@ -1,12 +1,8 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public class BarCommandSettings : FooCommandSettings
{
public class BarCommandSettings : FooCommandSettings
{
[CommandArgument(0, "<CORGI>")]
[Description("The corgi value.")]
public string Corgi { get; set; }
}
}
[CommandArgument(0, "<CORGI>")]
[Description("The corgi value.")]
public string Corgi { get; set; }
}

View File

@ -1,15 +1,11 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public class CatSettings : MammalSettings
{
public class CatSettings : MammalSettings
{
[CommandOption("--agility <VALUE>")]
[TypeConverter(typeof(CatAgilityConverter))]
[DefaultValue(10)]
[Description("The agility between 0 and 100.")]
[PositiveNumberValidator("Agility cannot be negative.")]
public int Agility { get; set; }
}
}
[CommandOption("--agility <VALUE>")]
[TypeConverter(typeof(CatAgilityConverter))]
[DefaultValue(10)]
[Description("The agility between 0 and 100.")]
[PositiveNumberValidator("Agility cannot be negative.")]
public int Agility { get; set; }
}

View File

@ -1,23 +1,20 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class DogSettings : MammalSettings
{
public sealed class DogSettings : MammalSettings
[CommandArgument(0, "<AGE>")]
public int Age { get; set; }
[CommandOption("-g|--good-boy")]
public bool GoodBoy { get; set; }
public override ValidationResult Validate()
{
[CommandArgument(0, "<AGE>")]
public int Age { get; set; }
[CommandOption("-g|--good-boy")]
public bool GoodBoy { get; set; }
public override ValidationResult Validate()
if (Name == "Tiger")
{
if (Name == "Tiger")
{
return ValidationResult.Error("Tiger is not a dog name!");
}
return ValidationResult.Success();
return ValidationResult.Error("Tiger is not a dog name!");
}
return ValidationResult.Success();
}
}
}

View File

@ -1,8 +1,5 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class EmptySettings : CommandSettings
{
public sealed class EmptySettings : CommandSettings
{
}
}
}

View File

@ -1,12 +1,8 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public class FooCommandSettings : CommandSettings
{
public class FooCommandSettings : CommandSettings
{
[CommandArgument(0, "[QUX]")]
[Description("The qux value.")]
public string Qux { get; set; }
}
}
[CommandArgument(0, "[QUX]")]
[Description("The qux value.")]
public string Qux { get; set; }
}

View File

@ -1,12 +1,8 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class GiraffeSettings : MammalSettings
{
public sealed class GiraffeSettings : MammalSettings
{
[CommandArgument(0, "<LENGTH>")]
[Description("The option description.")]
public int Length { get; set; }
}
}
[CommandArgument(0, "<LENGTH>")]
[Description("The option description.")]
public int Length { get; set; }
}

View File

@ -1,20 +1,16 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class HiddenOptionSettings : CommandSettings
{
public sealed class HiddenOptionSettings : CommandSettings
{
[CommandArgument(0, "<FOO>")]
[Description("Dummy argument FOO")]
public int Foo { get; set; }
[CommandArgument(0, "<FOO>")]
[Description("Dummy argument FOO")]
public int Foo { get; set; }
[CommandOption("--bar", IsHidden = true)]
[Description("You should not be able to read this unless you used the 'cli explain' command with the '--hidden' option")]
public int Bar { get; set; }
[CommandOption("--bar", IsHidden = true)]
[Description("You should not be able to read this unless you used the 'cli explain' command with the '--hidden' option")]
public int Bar { get; set; }
[CommandOption("--baz")]
[Description("Dummy option BAZ")]
public int Baz { get; set; }
}
}
[CommandOption("--baz")]
[Description("Dummy option BAZ")]
public int Baz { get; set; }
}

View File

@ -1,10 +1,7 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class InvalidSettings : CommandSettings
{
public sealed class InvalidSettings : CommandSettings
{
[CommandOption("-f|--foo [BAR]")]
public string Value { get; set; }
}
}
[CommandOption("-f|--foo [BAR]")]
public string Value { get; set; }
}

View File

@ -1,16 +1,12 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public class LionSettings : CatSettings
{
public class LionSettings : CatSettings
{
[CommandArgument(0, "<TEETH>")]
[Description("The number of teeth the lion has.")]
public int Teeth { get; set; }
[CommandArgument(0, "<TEETH>")]
[Description("The number of teeth the lion has.")]
public int Teeth { get; set; }
[CommandOption("-c <CHILDREN>")]
[Description("The number of children the lion has.")]
public int Children { get; set; }
}
}
[CommandOption("-c <CHILDREN>")]
[Description("The number of children the lion has.")]
public int Children { get; set; }
}

View File

@ -1,10 +1,7 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public class MammalSettings : AnimalSettings
{
public class MammalSettings : AnimalSettings
{
[CommandOption("-n|-p|--name|--pet-name <VALUE>")]
public string Name { get; set; }
}
}
[CommandOption("-n|-p|--name|--pet-name <VALUE>")]
public string Name { get; set; }
}

View File

@ -1,22 +1,19 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public class MultipleArgumentVectorSettings : CommandSettings
{
public class MultipleArgumentVectorSettings : CommandSettings
{
[CommandArgument(0, "<Foos>")]
public string[] Foo { get; set; }
[CommandArgument(0, "<Foos>")]
public string[] Foo { get; set; }
[CommandArgument(0, "<Bars>")]
public string[] Bar { get; set; }
}
[CommandArgument(0, "<Bars>")]
public string[] Bar { get; set; }
}
public class MultipleArgumentVectorSpecifiedFirstSettings : CommandSettings
{
[CommandArgument(1, "[Bar]")]
public string Bar { get; set; }
public class MultipleArgumentVectorSpecifiedFirstSettings : CommandSettings
{
[CommandArgument(1, "[Bar]")]
public string Bar { get; set; }
[CommandArgument(0, "<Foos>")]
public string[] Foo { get; set; }
}
}
[CommandArgument(0, "<Foos>")]
public string[] Foo { get; set; }
}

View File

@ -1,13 +1,10 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public class OptionVectorSettings : CommandSettings
{
public class OptionVectorSettings : CommandSettings
{
[CommandOption("--foo")]
public string[] Foo { get; set; }
[CommandOption("--foo")]
public string[] Foo { get; set; }
[CommandOption("--bar")]
public int[] Bar { get; set; }
}
}
[CommandOption("--bar")]
public int[] Bar { get; set; }
}

View File

@ -1,40 +1,35 @@
using System;
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class OptionalArgumentWithDefaultValueSettings : CommandSettings
{
public sealed class OptionalArgumentWithDefaultValueSettings : CommandSettings
{
[CommandArgument(0, "[GREETING]")]
[DefaultValue("Hello World")]
public string Greeting { get; set; }
}
[CommandArgument(0, "[GREETING]")]
[DefaultValue("Hello World")]
public string Greeting { get; set; }
}
public sealed class OptionalArgumentWithPropertyInitializerSettings : CommandSettings
{
[CommandArgument(0, "[NAMES]")]
public string[] Names { get; set; } = Array.Empty<string>();
public sealed class OptionalArgumentWithPropertyInitializerSettings : CommandSettings
{
[CommandArgument(0, "[NAMES]")]
public string[] Names { get; set; } = Array.Empty<string>();
[CommandOption("-c")]
public int Count { get; set; } = 1;
[CommandOption("-c")]
public int Count { get; set; } = 1;
[CommandOption("-v")]
public int Value { get; set; } = 0;
}
[CommandOption("-v")]
public int Value { get; set; } = 0;
}
public sealed class OptionalArgumentWithDefaultValueAndTypeConverterSettings : CommandSettings
{
[CommandArgument(0, "[GREETING]")]
[DefaultValue("5")]
[TypeConverter(typeof(StringToIntegerConverter))]
public int Greeting { get; set; }
}
public sealed class OptionalArgumentWithDefaultValueAndTypeConverterSettings : CommandSettings
{
[CommandArgument(0, "[GREETING]")]
[DefaultValue("5")]
[TypeConverter(typeof(StringToIntegerConverter))]
public int Greeting { get; set; }
}
public sealed class RequiredArgumentWithDefaultValueSettings : CommandSettings
{
[CommandArgument(0, "<GREETING>")]
[DefaultValue("Hello World")]
public string Greeting { get; set; }
}
}
public sealed class RequiredArgumentWithDefaultValueSettings : CommandSettings
{
[CommandArgument(0, "<GREETING>")]
[DefaultValue("Hello World")]
public string Greeting { get; set; }
}

View File

@ -1,10 +1,7 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
public sealed class StringOptionSettings : CommandSettings
{
public sealed class StringOptionSettings : CommandSettings
{
[CommandOption("-f|--foo")]
public string Foo { get; set; }
}
}
[CommandOption("-f|--foo")]
public string Foo { get; set; }
}

View File

@ -1,29 +1,25 @@
using System;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public sealed class EvenNumberValidatorAttribute : ParameterValidationAttribute
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public sealed class EvenNumberValidatorAttribute : ParameterValidationAttribute
public EvenNumberValidatorAttribute(string errorMessage)
: base(errorMessage)
{
public EvenNumberValidatorAttribute(string errorMessage)
: base(errorMessage)
{
}
}
public override ValidationResult Validate(CommandParameterContext context)
public override ValidationResult Validate(CommandParameterContext context)
{
if (context.Value is int integer)
{
if (context.Value is int integer)
if (integer % 2 == 0)
{
if (integer % 2 == 0)
{
return ValidationResult.Success();
}
return ValidationResult.Error($"Number is not even ({context.Parameter.PropertyName}).");
return ValidationResult.Success();
}
throw new InvalidOperationException($"Parameter is not a number ({context.Parameter.PropertyName}).");
return ValidationResult.Error($"Number is not even ({context.Parameter.PropertyName}).");
}
throw new InvalidOperationException($"Parameter is not a number ({context.Parameter.PropertyName}).");
}
}
}

View File

@ -1,29 +1,25 @@
using System;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data;
namespace Spectre.Console.Tests.Data
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public sealed class PositiveNumberValidatorAttribute : ParameterValidationAttribute
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public sealed class PositiveNumberValidatorAttribute : ParameterValidationAttribute
public PositiveNumberValidatorAttribute(string errorMessage)
: base(errorMessage)
{
public PositiveNumberValidatorAttribute(string errorMessage)
: base(errorMessage)
{
}
}
public override ValidationResult Validate(CommandParameterContext context)
public override ValidationResult Validate(CommandParameterContext context)
{
if (context.Value is int integer)
{
if (context.Value is int integer)
if (integer > 0)
{
if (integer > 0)
{
return ValidationResult.Success();
}
return ValidationResult.Error($"Number is not greater than 0 ({context.Parameter.PropertyName}).");
return ValidationResult.Success();
}
throw new InvalidOperationException($"Parameter is not a number ({context.Parameter.PropertyName}).");
return ValidationResult.Error($"Number is not greater than 0 ({context.Parameter.PropertyName}).");
}
throw new InvalidOperationException($"Parameter is not a number ({context.Parameter.PropertyName}).");
}
}
}

View File

@ -0,0 +1,24 @@
global using System;
global using System.Collections.Generic;
global using System.ComponentModel;
global using System.Diagnostics;
global using System.Diagnostics.CodeAnalysis;
global using System.Globalization;
global using System.IO;
global using System.Linq;
global using System.Reflection;
global using System.Runtime.CompilerServices;
global using System.Text.RegularExpressions;
global using System.Threading;
global using System.Threading.Tasks;
global using Shouldly;
global using Spectre.Console.Advanced;
global using Spectre.Console.Cli;
global using Spectre.Console.Cli.Unsafe;
global using Spectre.Console.Rendering;
global using Spectre.Console.Testing;
global using Spectre.Console.Tests.Data;
global using Spectre.Verify.Extensions;
global using VerifyTests;
global using VerifyXunit;
global using Xunit;

View File

@ -1,9 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<AdditionalFiles Include="..\..\src\stylecop.json" Link="Properties/stylecop.json" />
</ItemGroup>
<PropertyGroup>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('Windows'))">net6.0;net5.0;net48</TargetFrameworks>
<TargetFrameworks Condition="!$([MSBuild]::IsOSPlatform('Windows'))">net6.0;net5.0</TargetFrameworks>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
<ItemGroup>

View File

@ -1,79 +1,71 @@
using System.Threading.Tasks;
using Shouldly;
using Spectre.Console.Testing;
using Spectre.Verify.Extensions;
using VerifyXunit;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
[UsesVerify]
[ExpectationPath("AlternateScreen")]
public sealed class AlternateScreenTests
{
[UsesVerify]
[ExpectationPath("AlternateScreen")]
public sealed class AlternateScreenTests
[Fact]
public void Should_Throw_If_Alternative_Buffer_Is_Not_Supported_By_Terminal()
{
[Fact]
public void Should_Throw_If_Alternative_Buffer_Is_Not_Supported_By_Terminal()
// Given
var console = new TestConsole();
console.Profile.Capabilities.AlternateBuffer = false;
// When
var result = Record.Exception(() =>
{
// Given
var console = new TestConsole();
console.Profile.Capabilities.AlternateBuffer = false;
// When
var result = Record.Exception(() =>
{
console.WriteLine("Foo");
console.AlternateScreen(() =>
{
console.WriteLine("Bar");
});
});
// Then
result.ShouldNotBeNull();
result.Message.ShouldBe("Alternate buffers are not supported by your terminal.");
}
[Fact]
public void Should_Throw_If_Ansi_Is_Not_Supported_By_Terminal()
{
// Given
var console = new TestConsole();
console.Profile.Capabilities.Ansi = false;
console.Profile.Capabilities.AlternateBuffer = true;
// When
var result = Record.Exception(() =>
{
console.WriteLine("Foo");
console.AlternateScreen(() =>
{
console.WriteLine("Bar");
});
});
// Then
result.ShouldNotBeNull();
result.Message.ShouldBe("Alternate buffers are not supported since your terminal does not support ANSI.");
}
[Fact]
[Expectation("Show")]
public async Task Should_Write_To_Alternate_Screen()
{
// Given
var console = new TestConsole();
console.EmitAnsiSequences = true;
console.Profile.Capabilities.AlternateBuffer = true;
// When
console.WriteLine("Foo");
console.AlternateScreen(() =>
{
console.WriteLine("Bar");
});
});
// Then
await Verifier.Verify(console.Output);
}
// Then
result.ShouldNotBeNull();
result.Message.ShouldBe("Alternate buffers are not supported by your terminal.");
}
}
[Fact]
public void Should_Throw_If_Ansi_Is_Not_Supported_By_Terminal()
{
// Given
var console = new TestConsole();
console.Profile.Capabilities.Ansi = false;
console.Profile.Capabilities.AlternateBuffer = true;
// When
var result = Record.Exception(() =>
{
console.WriteLine("Foo");
console.AlternateScreen(() =>
{
console.WriteLine("Bar");
});
});
// Then
result.ShouldNotBeNull();
result.Message.ShouldBe("Alternate buffers are not supported since your terminal does not support ANSI.");
}
[Fact]
[Expectation("Show")]
public async Task Should_Write_To_Alternate_Screen()
{
// Given
var console = new TestConsole();
console.EmitAnsiSequences = true;
console.Profile.Capabilities.AlternateBuffer = true;
// When
console.WriteLine("Foo");
console.AlternateScreen(() =>
{
console.WriteLine("Bar");
});
// Then
await Verifier.Verify(console.Output);
}
}

View File

@ -1,61 +1,55 @@
using Shouldly;
using Spectre.Console.Advanced;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public sealed partial class AnsiConsoleTests
{
public sealed partial class AnsiConsoleTests
public sealed class Advanced
{
public sealed class Advanced
[Fact]
public void Should_Write_Ansi_Codes_To_Console_If_Supported()
{
[Fact]
public void Should_Write_Ansi_Codes_To_Console_If_Supported()
{
// Given
var console = new TestConsole()
.SupportsAnsi(true)
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// Given
var console = new TestConsole()
.SupportsAnsi(true)
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.WriteAnsi("Hello");
// When
console.WriteAnsi("Hello");
// Then
console.Output.NormalizeLineEndings()
.ShouldBe("Hello");
}
// Then
console.Output.NormalizeLineEndings()
.ShouldBe("Hello");
}
[Fact]
public void Should_Not_Write_Ansi_Codes_To_Console_If_Not_Supported()
{
// Given
var console = new TestConsole()
.SupportsAnsi(false)
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
[Fact]
public void Should_Not_Write_Ansi_Codes_To_Console_If_Not_Supported()
{
// Given
var console = new TestConsole()
.SupportsAnsi(false)
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.WriteAnsi("Hello");
// When
console.WriteAnsi("Hello");
// Then
console.Output.NormalizeLineEndings()
.ShouldBeEmpty();
}
// Then
console.Output.NormalizeLineEndings()
.ShouldBeEmpty();
}
[Fact]
public void Should_Return_Ansi_For_Renderable()
{
// Given
var console = new TestConsole().Colors(ColorSystem.TrueColor);
var markup = new Console.Markup("[yellow]Hello [blue]World[/]![/]");
[Fact]
public void Should_Return_Ansi_For_Renderable()
{
// Given
var console = new TestConsole().Colors(ColorSystem.TrueColor);
var markup = new Console.Markup("[yellow]Hello [blue]World[/]![/]");
// When
var result = console.ToAnsi(markup);
// When
var result = console.ToAnsi(markup);
// Then
result.ShouldBe("Hello World!");
}
// Then
result.ShouldBe("Hello World!");
}
}
}
}

View File

@ -1,248 +1,242 @@
using System.IO;
using Shouldly;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public partial class AnsiConsoleTests
{
public partial class AnsiConsoleTests
[Theory]
[InlineData(ColorSystemSupport.NoColors, ColorSystem.NoColors)]
[InlineData(ColorSystemSupport.Legacy, ColorSystem.Legacy)]
[InlineData(ColorSystemSupport.Standard, ColorSystem.Standard)]
[InlineData(ColorSystemSupport.EightBit, ColorSystem.EightBit)]
[InlineData(ColorSystemSupport.TrueColor, ColorSystem.TrueColor)]
public void Should_Create_Console_With_Requested_ColorSystem(ColorSystemSupport requested, ColorSystem expected)
{
// Given, When
var console = AnsiConsole.Create(new AnsiConsoleSettings
{
ColorSystem = requested,
Out = new AnsiConsoleOutput(new StringWriter()),
});
// Then
console.Profile.Capabilities.ColorSystem.ShouldBe(expected);
}
public sealed class TrueColor
{
[Theory]
[InlineData(ColorSystemSupport.NoColors, ColorSystem.NoColors)]
[InlineData(ColorSystemSupport.Legacy, ColorSystem.Legacy)]
[InlineData(ColorSystemSupport.Standard, ColorSystem.Standard)]
[InlineData(ColorSystemSupport.EightBit, ColorSystem.EightBit)]
[InlineData(ColorSystemSupport.TrueColor, ColorSystem.TrueColor)]
public void Should_Create_Console_With_Requested_ColorSystem(ColorSystemSupport requested, ColorSystem expected)
[InlineData(true, "\u001b[38;2;128;0;128mHello\u001b[0m")]
[InlineData(false, "\u001b[48;2;128;0;128mHello\u001b[0m")]
public void Should_Return_Correct_Code(bool foreground, string expected)
{
// Given, When
var console = AnsiConsole.Create(new AnsiConsoleSettings
{
ColorSystem = requested,
Out = new AnsiConsoleOutput(new StringWriter()),
});
// Given
var console = new TestConsole()
.Colors(ColorSystem.TrueColor)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(128, 0, 128), foreground));
// Then
console.Profile.Capabilities.ColorSystem.ShouldBe(expected);
console.Output.ShouldBe(expected);
}
public sealed class TrueColor
[Theory]
[InlineData(true, "\u001b[38;5;5mHello\u001b[0m")]
[InlineData(false, "\u001b[48;5;5mHello\u001b[0m")]
public void Should_Return_Eight_Bit_Ansi_Code_For_Known_Colors(bool foreground, string expected)
{
[Theory]
[InlineData(true, "\u001b[38;2;128;0;128mHello\u001b[0m")]
[InlineData(false, "\u001b[48;2;128;0;128mHello\u001b[0m")]
public void Should_Return_Correct_Code(bool foreground, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.TrueColor)
.EmitAnsiSequences();
// Given
var console = new TestConsole()
.Colors(ColorSystem.TrueColor)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(128, 0, 128), foreground));
// When
console.Write("Hello", new Style().SetColor(Color.Purple, foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, "\u001b[38;5;5mHello\u001b[0m")]
[InlineData(false, "\u001b[48;5;5mHello\u001b[0m")]
public void Should_Return_Eight_Bit_Ansi_Code_For_Known_Colors(bool foreground, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.TrueColor)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(Color.Purple, foreground));
// Then
console.Output.ShouldBe(expected);
}
}
public sealed class EightBit
{
[Theory]
[InlineData(true, "\u001b[38;5;3mHello\u001b[0m")]
[InlineData(false, "\u001b[48;5;3mHello\u001b[0m")]
public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.EightBit)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(Color.Olive, foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, "\u001b[38;5;3mHello\u001b[0m")]
[InlineData(false, "\u001b[48;5;3mHello\u001b[0m")]
public void Should_Map_TrueColor_To_Nearest_Eight_Bit_Color_If_Possible(bool foreground, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.EightBit)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(128, 128, 0), foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, "\u001b[38;5;3mHello\u001b[0m")]
[InlineData(false, "\u001b[48;5;3mHello\u001b[0m")]
public void Should_Estimate_TrueColor_To_Nearest_Eight_Bit_Color(bool foreground, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.EightBit)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(126, 127, 0), foreground));
// Then
console.Output.ShouldBe(expected);
}
}
public sealed class Standard
{
[Theory]
[InlineData(true, "\u001b[33mHello\u001b[0m")]
[InlineData(false, "\u001b[43mHello\u001b[0m")]
public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(Color.Olive, foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, 128, 128, 128, "\u001b[90mHello\u001b[0m")]
[InlineData(false, 128, 128, 128, "\u001b[100mHello\u001b[0m")]
[InlineData(true, 0, 128, 0, "\u001b[32mHello\u001b[0m")]
[InlineData(false, 0, 128, 0, "\u001b[42mHello\u001b[0m")]
public void Should_Map_TrueColor_To_Nearest_Four_Bit_Color_If_Possible(
bool foreground,
byte r, byte g, byte b,
string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, 112, 120, 128, "\u001b[90mHello\u001b[0m")]
[InlineData(false, 112, 120, 128, "\u001b[100mHello\u001b[0m")]
[InlineData(true, 0, 120, 12, "\u001b[32mHello\u001b[0m")]
[InlineData(false, 0, 120, 12, "\u001b[42mHello\u001b[0m")]
public void Should_Estimate_TrueColor_To_Nearest_Four_Bit_Color(
bool foreground,
byte r, byte g, byte b,
string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground));
// Then
console.Output.ShouldBe(expected);
}
}
public sealed class Legacy
{
[Theory]
[InlineData(true, "\u001b[33mHello\u001b[0m")]
[InlineData(false, "\u001b[43mHello\u001b[0m")]
public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Legacy)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(Color.Olive, foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, 128, 128, 128, "\u001b[37mHello\u001b[0m")]
[InlineData(false, 128, 128, 128, "\u001b[47mHello\u001b[0m")]
[InlineData(true, 0, 128, 0, "\u001b[32mHello\u001b[0m")]
[InlineData(false, 0, 128, 0, "\u001b[42mHello\u001b[0m")]
public void Should_Map_TrueColor_To_Nearest_Three_Bit_Color_If_Possible(
bool foreground,
byte r, byte g, byte b,
string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Legacy)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, 112, 120, 128, "\u001b[36mHello\u001b[0m")]
[InlineData(false, 112, 120, 128, "\u001b[46mHello\u001b[0m")]
[InlineData(true, 0, 120, 12, "\u001b[32mHello\u001b[0m")]
[InlineData(false, 0, 120, 12, "\u001b[42mHello\u001b[0m")]
public void Should_Estimate_TrueColor_To_Nearest_Three_Bit_Color(
bool foreground,
byte r, byte g, byte b,
string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Legacy)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground));
// Then
console.Output.ShouldBe(expected);
}
// Then
console.Output.ShouldBe(expected);
}
}
}
public sealed class EightBit
{
[Theory]
[InlineData(true, "\u001b[38;5;3mHello\u001b[0m")]
[InlineData(false, "\u001b[48;5;3mHello\u001b[0m")]
public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.EightBit)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(Color.Olive, foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, "\u001b[38;5;3mHello\u001b[0m")]
[InlineData(false, "\u001b[48;5;3mHello\u001b[0m")]
public void Should_Map_TrueColor_To_Nearest_Eight_Bit_Color_If_Possible(bool foreground, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.EightBit)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(128, 128, 0), foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, "\u001b[38;5;3mHello\u001b[0m")]
[InlineData(false, "\u001b[48;5;3mHello\u001b[0m")]
public void Should_Estimate_TrueColor_To_Nearest_Eight_Bit_Color(bool foreground, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.EightBit)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(126, 127, 0), foreground));
// Then
console.Output.ShouldBe(expected);
}
}
public sealed class Standard
{
[Theory]
[InlineData(true, "\u001b[33mHello\u001b[0m")]
[InlineData(false, "\u001b[43mHello\u001b[0m")]
public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(Color.Olive, foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, 128, 128, 128, "\u001b[90mHello\u001b[0m")]
[InlineData(false, 128, 128, 128, "\u001b[100mHello\u001b[0m")]
[InlineData(true, 0, 128, 0, "\u001b[32mHello\u001b[0m")]
[InlineData(false, 0, 128, 0, "\u001b[42mHello\u001b[0m")]
public void Should_Map_TrueColor_To_Nearest_Four_Bit_Color_If_Possible(
bool foreground,
byte r, byte g, byte b,
string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, 112, 120, 128, "\u001b[90mHello\u001b[0m")]
[InlineData(false, 112, 120, 128, "\u001b[100mHello\u001b[0m")]
[InlineData(true, 0, 120, 12, "\u001b[32mHello\u001b[0m")]
[InlineData(false, 0, 120, 12, "\u001b[42mHello\u001b[0m")]
public void Should_Estimate_TrueColor_To_Nearest_Four_Bit_Color(
bool foreground,
byte r, byte g, byte b,
string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground));
// Then
console.Output.ShouldBe(expected);
}
}
public sealed class Legacy
{
[Theory]
[InlineData(true, "\u001b[33mHello\u001b[0m")]
[InlineData(false, "\u001b[43mHello\u001b[0m")]
public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Legacy)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(Color.Olive, foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, 128, 128, 128, "\u001b[37mHello\u001b[0m")]
[InlineData(false, 128, 128, 128, "\u001b[47mHello\u001b[0m")]
[InlineData(true, 0, 128, 0, "\u001b[32mHello\u001b[0m")]
[InlineData(false, 0, 128, 0, "\u001b[42mHello\u001b[0m")]
public void Should_Map_TrueColor_To_Nearest_Three_Bit_Color_If_Possible(
bool foreground,
byte r, byte g, byte b,
string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Legacy)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(true, 112, 120, 128, "\u001b[36mHello\u001b[0m")]
[InlineData(false, 112, 120, 128, "\u001b[46mHello\u001b[0m")]
[InlineData(true, 0, 120, 12, "\u001b[32mHello\u001b[0m")]
[InlineData(false, 0, 120, 12, "\u001b[42mHello\u001b[0m")]
public void Should_Estimate_TrueColor_To_Nearest_Three_Bit_Color(
bool foreground,
byte r, byte g, byte b,
string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Legacy)
.EmitAnsiSequences();
// When
console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground));
// Then
console.Output.ShouldBe(expected);
}
}
}

View File

@ -1,52 +1,47 @@
using Shouldly;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public partial class AnsiConsoleTests
{
public partial class AnsiConsoleTests
public sealed class Cursor
{
public sealed class Cursor
public sealed class TheMoveMethod
{
public sealed class TheMoveMethod
[Theory]
[InlineData(CursorDirection.Up, "HelloWorld")]
[InlineData(CursorDirection.Down, "HelloWorld")]
[InlineData(CursorDirection.Right, "HelloWorld")]
[InlineData(CursorDirection.Left, "HelloWorld")]
public void Should_Return_Correct_Ansi_Code(CursorDirection direction, string expected)
{
[Theory]
[InlineData(CursorDirection.Up, "HelloWorld")]
[InlineData(CursorDirection.Down, "HelloWorld")]
[InlineData(CursorDirection.Right, "HelloWorld")]
[InlineData(CursorDirection.Left, "HelloWorld")]
public void Should_Return_Correct_Ansi_Code(CursorDirection direction, string expected)
{
// Given
var console = new TestConsole().EmitAnsiSequences();
// Given
var console = new TestConsole().EmitAnsiSequences();
// When
console.Write("Hello");
console.Cursor.Move(direction, 2);
console.Write("World");
// When
console.Write("Hello");
console.Cursor.Move(direction, 2);
console.Write("World");
// Then
console.Output.ShouldBe(expected);
}
// Then
console.Output.ShouldBe(expected);
}
}
public sealed class TheSetPositionMethod
public sealed class TheSetPositionMethod
{
[Fact]
public void Should_Return_Correct_Ansi_Code()
{
[Fact]
public void Should_Return_Correct_Ansi_Code()
{
// Given
var console = new TestConsole().EmitAnsiSequences();
// Given
var console = new TestConsole().EmitAnsiSequences();
// When
console.Write("Hello");
console.Cursor.SetPosition(5, 3);
console.Write("World");
// When
console.Write("Hello");
console.Cursor.SetPosition(5, 3);
console.Write("World");
// Then
console.Output.ShouldBe("HelloWorld");
}
// Then
console.Output.ShouldBe("HelloWorld");
}
}
}
}
}

View File

@ -1,120 +1,114 @@
using System;
using Shouldly;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public partial class AnsiConsoleTests
{
public partial class AnsiConsoleTests
public sealed class Markup
{
public sealed class Markup
[Theory]
[InlineData("[yellow]Hello[/]", "Hello")]
[InlineData("[yellow]Hello [italic]World[/]![/]", "Hello World!")]
public void Should_Output_Expected_Ansi_For_Markup(string markup, string expected)
{
[Theory]
[InlineData("[yellow]Hello[/]", "Hello")]
[InlineData("[yellow]Hello [italic]World[/]![/]", "Hello World!")]
public void Should_Output_Expected_Ansi_For_Markup(string markup, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Markup(markup);
// When
console.Markup(markup);
// Then
console.Output.ShouldBe(expected);
}
// Then
console.Output.ShouldBe(expected);
}
[Fact]
public void Should_Output_Expected_Ansi_For_Link_With_Url_And_Text()
{
// Given
var console = new TestConsole()
.EmitAnsiSequences();
[Fact]
public void Should_Output_Expected_Ansi_For_Link_With_Url_And_Text()
{
// Given
var console = new TestConsole()
.EmitAnsiSequences();
// When
console.Markup("[link=https://patriksvensson.se]Click to visit my blog[/]");
// When
console.Markup("[link=https://patriksvensson.se]Click to visit my blog[/]");
// Then
console.Output.ShouldMatch("]8;id=[0-9]*;https:\\/\\/patriksvensson\\.se\\\\Click to visit my blog]8;;\\\\");
}
// Then
console.Output.ShouldMatch("]8;id=[0-9]*;https:\\/\\/patriksvensson\\.se\\\\Click to visit my blog]8;;\\\\");
}
[Fact]
public void Should_Output_Expected_Ansi_For_Link_With_Only_Url()
{
// Given
var console = new TestConsole()
.EmitAnsiSequences();
[Fact]
public void Should_Output_Expected_Ansi_For_Link_With_Only_Url()
{
// Given
var console = new TestConsole()
.EmitAnsiSequences();
// When
console.Markup("[link]https://patriksvensson.se[/]");
// When
console.Markup("[link]https://patriksvensson.se[/]");
// Then
console.Output.ShouldMatch("]8;id=[0-9]*;https:\\/\\/patriksvensson\\.se\\\\https:\\/\\/patriksvensson\\.se]8;;\\\\");
}
// Then
console.Output.ShouldMatch("]8;id=[0-9]*;https:\\/\\/patriksvensson\\.se\\\\https:\\/\\/patriksvensson\\.se]8;;\\\\");
}
[Theory]
[InlineData("[yellow]Hello [[ World[/]", "Hello [ World")]
public void Should_Be_Able_To_Escape_Tags(string markup, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
[Theory]
[InlineData("[yellow]Hello [[ World[/]", "Hello [ World")]
public void Should_Be_Able_To_Escape_Tags(string markup, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Markup(markup);
// When
console.Markup(markup);
// Then
console.Output.ShouldBe(expected);
}
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData("[yellow]Hello[", "Encountered malformed markup tag at position 14.")]
[InlineData("[yellow]Hello[/", "Encountered malformed markup tag at position 15.")]
[InlineData("[yellow]Hello[/foo", "Encountered malformed markup tag at position 15.")]
[InlineData("[yellow Hello", "Encountered malformed markup tag at position 13.")]
public void Should_Throw_If_Encounters_Malformed_Tag(string markup, string expected)
{
// Given
var console = new TestConsole();
[Theory]
[InlineData("[yellow]Hello[", "Encountered malformed markup tag at position 14.")]
[InlineData("[yellow]Hello[/", "Encountered malformed markup tag at position 15.")]
[InlineData("[yellow]Hello[/foo", "Encountered malformed markup tag at position 15.")]
[InlineData("[yellow Hello", "Encountered malformed markup tag at position 13.")]
public void Should_Throw_If_Encounters_Malformed_Tag(string markup, string expected)
{
// Given
var console = new TestConsole();
// When
var result = Record.Exception(() => console.Markup(markup));
// When
var result = Record.Exception(() => console.Markup(markup));
// Then
result.ShouldBeOfType<InvalidOperationException>()
.Message.ShouldBe(expected);
}
// Then
result.ShouldBeOfType<InvalidOperationException>()
.Message.ShouldBe(expected);
}
[Fact]
public void Should_Throw_If_Tags_Are_Unbalanced()
{
// Given
var console = new TestConsole();
[Fact]
public void Should_Throw_If_Tags_Are_Unbalanced()
{
// Given
var console = new TestConsole();
// When
var result = Record.Exception(() => console.Markup("[yellow][blue]Hello[/]"));
// When
var result = Record.Exception(() => console.Markup("[yellow][blue]Hello[/]"));
// Then
result.ShouldBeOfType<InvalidOperationException>()
.Message.ShouldBe("Unbalanced markup stack. Did you forget to close a tag?");
}
// Then
result.ShouldBeOfType<InvalidOperationException>()
.Message.ShouldBe("Unbalanced markup stack. Did you forget to close a tag?");
}
[Fact]
public void Should_Throw_If_Encounters_Closing_Tag()
{
// Given
var console = new TestConsole();
[Fact]
public void Should_Throw_If_Encounters_Closing_Tag()
{
// Given
var console = new TestConsole();
// When
var result = Record.Exception(() => console.Markup("Hello[/]World"));
// When
var result = Record.Exception(() => console.Markup("Hello[/]World"));
// Then
result.ShouldBeOfType<InvalidOperationException>()
.Message.ShouldBe("Encountered closing tag when none was expected near position 5.");
}
// Then
result.ShouldBeOfType<InvalidOperationException>()
.Message.ShouldBe("Encountered closing tag when none was expected near position 5.");
}
}
}
}

View File

@ -1,29 +1,23 @@
using System;
using Shouldly;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public partial class AnsiConsoleTests
{
public partial class AnsiConsoleTests
public sealed class Prompt
{
public sealed class Prompt
[Theory]
[InlineData(true, true)]
[InlineData(false, false)]
public void Should_Return_Default_Value_If_Nothing_Is_Entered(bool expected, bool defaultValue)
{
[Theory]
[InlineData(true, true)]
[InlineData(false, false)]
public void Should_Return_Default_Value_If_Nothing_Is_Entered(bool expected, bool defaultValue)
{
// Given
var console = new TestConsole().EmitAnsiSequences();
console.Input.PushKey(ConsoleKey.Enter);
// Given
var console = new TestConsole().EmitAnsiSequences();
console.Input.PushKey(ConsoleKey.Enter);
// When
var result = console.Confirm("Want some prompt?", defaultValue);
// When
var result = console.Confirm("Want some prompt?", defaultValue);
// Then
result.ShouldBe(expected);
}
// Then
result.ShouldBe(expected);
}
}
}
}

View File

@ -1,48 +1,43 @@
using Shouldly;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public partial class AnsiConsoleTests
{
public partial class AnsiConsoleTests
[Theory]
[InlineData(Decoration.Bold, "\u001b[1mHello World")]
[InlineData(Decoration.Dim, "\u001b[2mHello World")]
[InlineData(Decoration.Italic, "\u001b[3mHello World")]
[InlineData(Decoration.Underline, "\u001b[4mHello World")]
[InlineData(Decoration.Invert, "\u001b[7mHello World")]
[InlineData(Decoration.Conceal, "\u001b[8mHello World")]
[InlineData(Decoration.SlowBlink, "\u001b[5mHello World")]
[InlineData(Decoration.RapidBlink, "\u001b[6mHello World")]
[InlineData(Decoration.Strikethrough, "\u001b[9mHello World")]
public void Should_Write_Decorated_Text_Correctly(Decoration decoration, string expected)
{
[Theory]
[InlineData(Decoration.Bold, "\u001b[1mHello World")]
[InlineData(Decoration.Dim, "\u001b[2mHello World")]
[InlineData(Decoration.Italic, "\u001b[3mHello World")]
[InlineData(Decoration.Underline, "\u001b[4mHello World")]
[InlineData(Decoration.Invert, "\u001b[7mHello World")]
[InlineData(Decoration.Conceal, "\u001b[8mHello World")]
[InlineData(Decoration.SlowBlink, "\u001b[5mHello World")]
[InlineData(Decoration.RapidBlink, "\u001b[6mHello World")]
[InlineData(Decoration.Strikethrough, "\u001b[9mHello World")]
public void Should_Write_Decorated_Text_Correctly(Decoration decoration, string expected)
{
// Given
var console = new TestConsole()
.EmitAnsiSequences();
// Given
var console = new TestConsole()
.EmitAnsiSequences();
// When
console.Write("Hello World", new Style().Decoration(decoration));
// When
console.Write("Hello World", new Style().Decoration(decoration));
// Then
console.Output.ShouldBe(expected);
}
[Theory]
[InlineData(Decoration.Bold | Decoration.Underline, "\u001b[1;4mHello World")]
[InlineData(Decoration.Bold | Decoration.Underline | Decoration.Conceal, "\u001b[1;4;8mHello World")]
public void Should_Write_Text_With_Multiple_Decorations_Correctly(Decoration decoration, string expected)
{
// Given
var console = new TestConsole()
.EmitAnsiSequences();
// When
console.Write("Hello World", new Style().Decoration(decoration));
// Then
console.Output.ShouldBe(expected);
}
// Then
console.Output.ShouldBe(expected);
}
}
[Theory]
[InlineData(Decoration.Bold | Decoration.Underline, "\u001b[1;4mHello World")]
[InlineData(Decoration.Bold | Decoration.Underline | Decoration.Conceal, "\u001b[1;4;8mHello World")]
public void Should_Write_Text_With_Multiple_Decorations_Correctly(Decoration decoration, string expected)
{
// Given
var console = new TestConsole()
.EmitAnsiSequences();
// When
console.Write("Hello World", new Style().Decoration(decoration));
// Then
console.Output.ShouldBe(expected);
}
}

View File

@ -1,151 +1,145 @@
using System;
using Shouldly;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public partial class AnsiConsoleTests
{
public partial class AnsiConsoleTests
public sealed class Clear
{
public sealed class Clear
[Theory]
[InlineData(false, "HelloWorld")]
[InlineData(true, "HelloWorld")]
public void Should_Clear_Screen(bool home, string expected)
{
[Theory]
[InlineData(false, "HelloWorld")]
[InlineData(true, "HelloWorld")]
public void Should_Clear_Screen(bool home, string expected)
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write("Hello");
console.Clear(home);
console.Write("World");
// When
console.Write("Hello");
console.Clear(home);
console.Write("World");
// Then
console.Output.ShouldBe(expected);
}
}
public sealed class Write
{
[Fact]
public void Should_Combine_Decoration_And_Colors()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write(
"Hello",
new Style()
.Foreground(Color.RoyalBlue1)
.Background(Color.NavajoWhite1)
.Decoration(Decoration.Italic));
// Then
console.Output.ShouldBe("\u001b[3;90;47mHello\u001b[0m");
}
[Fact]
public void Should_Not_Include_Foreground_If_Set_To_Default_Color()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write(
"Hello",
new Style()
.Foreground(Color.Default)
.Background(Color.NavajoWhite1)
.Decoration(Decoration.Italic));
// Then
console.Output.ShouldBe("\u001b[3;47mHello\u001b[0m");
}
[Fact]
public void Should_Not_Include_Background_If_Set_To_Default_Color()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write(
"Hello",
new Style()
.Foreground(Color.RoyalBlue1)
.Background(Color.Default)
.Decoration(Decoration.Italic));
// Then
console.Output.ShouldBe("\u001b[3;90mHello\u001b[0m");
}
[Fact]
public void Should_Not_Include_Decoration_If_Set_To_None()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write(
"Hello",
new Style()
.Foreground(Color.RoyalBlue1)
.Background(Color.NavajoWhite1)
.Decoration(Decoration.None));
// Then
console.Output.ShouldBe("\u001b[90;47mHello\u001b[0m");
}
}
public sealed class WriteLine
{
[Fact]
public void Should_Reset_Colors_Correctly_After_Line_Break()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.WriteLine("Hello", new Style().Background(ConsoleColor.Red));
console.WriteLine("World", new Style().Background(ConsoleColor.Green));
// Then
console.Output.NormalizeLineEndings()
.ShouldBe("Hello\nWorld\n");
}
[Fact]
public void Should_Reset_Colors_Correctly_After_Line_Break_In_Text()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.WriteLine("Hello\nWorld", new Style().Background(ConsoleColor.Red));
// Then
console.Output.NormalizeLineEndings()
.ShouldBe("Hello\nWorld\n");
}
// Then
console.Output.ShouldBe(expected);
}
}
}
public sealed class Write
{
[Fact]
public void Should_Combine_Decoration_And_Colors()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write(
"Hello",
new Style()
.Foreground(Color.RoyalBlue1)
.Background(Color.NavajoWhite1)
.Decoration(Decoration.Italic));
// Then
console.Output.ShouldBe("\u001b[3;90;47mHello\u001b[0m");
}
[Fact]
public void Should_Not_Include_Foreground_If_Set_To_Default_Color()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write(
"Hello",
new Style()
.Foreground(Color.Default)
.Background(Color.NavajoWhite1)
.Decoration(Decoration.Italic));
// Then
console.Output.ShouldBe("\u001b[3;47mHello\u001b[0m");
}
[Fact]
public void Should_Not_Include_Background_If_Set_To_Default_Color()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write(
"Hello",
new Style()
.Foreground(Color.RoyalBlue1)
.Background(Color.Default)
.Decoration(Decoration.Italic));
// Then
console.Output.ShouldBe("\u001b[3;90mHello\u001b[0m");
}
[Fact]
public void Should_Not_Include_Decoration_If_Set_To_None()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.Write(
"Hello",
new Style()
.Foreground(Color.RoyalBlue1)
.Background(Color.NavajoWhite1)
.Decoration(Decoration.None));
// Then
console.Output.ShouldBe("\u001b[90;47mHello\u001b[0m");
}
}
public sealed class WriteLine
{
[Fact]
public void Should_Reset_Colors_Correctly_After_Line_Break()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.WriteLine("Hello", new Style().Background(ConsoleColor.Red));
console.WriteLine("World", new Style().Background(ConsoleColor.Green));
// Then
console.Output.NormalizeLineEndings()
.ShouldBe("Hello\nWorld\n");
}
[Fact]
public void Should_Reset_Colors_Correctly_After_Line_Break_In_Text()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
// When
console.WriteLine("Hello\nWorld", new Style().Background(ConsoleColor.Red));
// Then
console.Output.NormalizeLineEndings()
.ShouldBe("Hello\nWorld\n");
}
}
}

View File

@ -1,97 +1,88 @@
using System.Threading.Tasks;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Spectre.Verify.Extensions;
using VerifyXunit;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli.Annotations;
namespace Spectre.Console.Tests.Unit.Cli.Annotations
[ExpectationPath("Cli/Arguments")]
public sealed partial class CommandArgumentAttributeTests
{
[ExpectationPath("Cli/Arguments")]
public sealed partial class CommandArgumentAttributeTests
[UsesVerify]
public sealed class ArgumentCannotContainOptions
{
[UsesVerify]
public sealed class ArgumentCannotContainOptions
public sealed class Settings : CommandSettings
{
public sealed class Settings : CommandSettings
{
[CommandArgument(0, "--foo <BAR>")]
public string Foo { get; set; }
}
[Fact]
[Expectation("ArgumentCannotContainOptions")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
return Verifier.Verify(result);
}
[CommandArgument(0, "--foo <BAR>")]
public string Foo { get; set; }
}
[UsesVerify]
public sealed class MultipleValuesAreNotSupported
[Fact]
[Expectation("ArgumentCannotContainOptions")]
public Task Should_Return_Correct_Text()
{
public sealed class Settings : CommandSettings
{
[CommandArgument(0, "<FOO> <BAR>")]
public string Foo { get; set; }
}
// Given, When
var result = Fixture.Run<Settings>();
[Fact]
[Expectation("MultipleValuesAreNotSupported")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
return Verifier.Verify(result);
}
}
// Then
return Verifier.Verify(result);
}
[UsesVerify]
public sealed class MultipleValuesAreNotSupported
{
public sealed class Settings : CommandSettings
{
[CommandArgument(0, "<FOO> <BAR>")]
public string Foo { get; set; }
}
[UsesVerify]
public sealed class ValuesMustHaveName
[Fact]
[Expectation("MultipleValuesAreNotSupported")]
public Task Should_Return_Correct_Text()
{
public sealed class Settings : CommandSettings
{
[CommandArgument(0, "<>")]
public string Foo { get; set; }
}
// Given, When
var result = Fixture.Run<Settings>();
[Fact]
[Expectation("ValuesMustHaveName")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
return Verifier.Verify(result);
}
}
// Then
return Verifier.Verify(result);
}
[UsesVerify]
public sealed class ValuesMustHaveName
{
public sealed class Settings : CommandSettings
{
[CommandArgument(0, "<>")]
public string Foo { get; set; }
}
private static class Fixture
[Fact]
[Expectation("ValuesMustHaveName")]
public Task Should_Return_Correct_Text()
{
public static string Run<TSettings>(params string[] args)
where TSettings : CommandSettings
{
using (var writer = new TestConsole())
{
var app = new CommandApp();
app.Configure(c => c.ConfigureConsole(writer));
app.Configure(c => c.AddCommand<GenericCommand<TSettings>>("foo"));
app.Run(args);
// Given, When
var result = Fixture.Run<Settings>();
return writer.Output
.NormalizeLineEndings()
.TrimLines()
.Trim();
}
// Then
return Verifier.Verify(result);
}
}
private static class Fixture
{
public static string Run<TSettings>(params string[] args)
where TSettings : CommandSettings
{
using (var writer = new TestConsole())
{
var app = new CommandApp();
app.Configure(c => c.ConfigureConsole(writer));
app.Configure(c => c.AddCommand<GenericCommand<TSettings>>("foo"));
app.Run(args);
return writer.Output
.NormalizeLineEndings()
.TrimLines()
.Trim();
}
}
}
}
}

View File

@ -1,64 +1,59 @@
using Shouldly;
using Spectre.Console.Cli;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli.Annotations;
namespace Spectre.Console.Tests.Unit.Cli.Annotations
public sealed partial class CommandArgumentAttributeTests
{
public sealed partial class CommandArgumentAttributeTests
[Fact]
public void Should_Not_Contain_Options()
{
[Fact]
public void Should_Not_Contain_Options()
{
// Given, When
var result = Record.Exception(() => new CommandArgumentAttribute(0, "--foo <BAR>"));
// Given, When
var result = Record.Exception(() => new CommandArgumentAttribute(0, "--foo <BAR>"));
// Then
result.ShouldNotBe(null);
result.ShouldBeOfType<CommandTemplateException>().And(exception =>
exception.Message.ShouldBe("Arguments can not contain options."));
}
[Theory]
[InlineData("<FOO> <BAR>")]
[InlineData("[FOO] [BAR]")]
[InlineData("[FOO] <BAR>")]
[InlineData("<FOO> [BAR]")]
public void Should_Not_Contain_Multiple_Value_Names(string template)
{
// Given, When
var result = Record.Exception(() => new CommandArgumentAttribute(0, template));
// Then
result.ShouldNotBe(null);
result.ShouldBeOfType<CommandTemplateException>().And(exception =>
exception.Message.ShouldBe("Multiple values are not supported."));
}
[Theory]
[InlineData("<>")]
[InlineData("[]")]
public void Should_Not_Contain_Empty_Value_Name(string template)
{
// Given, When
var result = Record.Exception(() => new CommandArgumentAttribute(0, template));
// Then
result.ShouldNotBe(null);
result.ShouldBeOfType<CommandTemplateException>().And(exception =>
exception.Message.ShouldBe("Values without name are not allowed."));
}
[Theory]
[InlineData("<FOO>", true)]
[InlineData("[FOO]", false)]
public void Should_Parse_Valid_Options(string template, bool required)
{
// Given, When
var result = new CommandArgumentAttribute(0, template);
// Then
result.ValueName.ShouldBe("FOO");
result.IsRequired.ShouldBe(required);
}
// Then
result.ShouldNotBe(null);
result.ShouldBeOfType<CommandTemplateException>().And(exception =>
exception.Message.ShouldBe("Arguments can not contain options."));
}
}
[Theory]
[InlineData("<FOO> <BAR>")]
[InlineData("[FOO] [BAR]")]
[InlineData("[FOO] <BAR>")]
[InlineData("<FOO> [BAR]")]
public void Should_Not_Contain_Multiple_Value_Names(string template)
{
// Given, When
var result = Record.Exception(() => new CommandArgumentAttribute(0, template));
// Then
result.ShouldNotBe(null);
result.ShouldBeOfType<CommandTemplateException>().And(exception =>
exception.Message.ShouldBe("Multiple values are not supported."));
}
[Theory]
[InlineData("<>")]
[InlineData("[]")]
public void Should_Not_Contain_Empty_Value_Name(string template)
{
// Given, When
var result = Record.Exception(() => new CommandArgumentAttribute(0, template));
// Then
result.ShouldNotBe(null);
result.ShouldBeOfType<CommandTemplateException>().And(exception =>
exception.Message.ShouldBe("Values without name are not allowed."));
}
[Theory]
[InlineData("<FOO>", true)]
[InlineData("[FOO]", false)]
public void Should_Parse_Valid_Options(string template, bool required)
{
// Given, When
var result = new CommandArgumentAttribute(0, template);
// Then
result.ValueName.ShouldBe("FOO");
result.IsRequired.ShouldBe(required);
}
}

View File

@ -1,250 +1,240 @@
using System.Threading.Tasks;
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Spectre.Verify.Extensions;
using VerifyXunit;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli.Annotations;
namespace Spectre.Console.Tests.Unit.Cli.Annotations
[ExpectationPath("Cli/Arguments")]
public sealed partial class CommandOptionAttributeTests
{
[ExpectationPath("Cli/Arguments")]
public sealed partial class CommandOptionAttributeTests
[UsesVerify]
public sealed class UnexpectedCharacter
{
[UsesVerify]
public sealed class UnexpectedCharacter
public sealed class Settings : CommandSettings
{
public sealed class Settings : CommandSettings
{
[CommandOption("<FOO> $ <BAR>")]
public string Foo { get; set; }
}
[Fact]
[Expectation("UnexpectedCharacter")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Encountered unexpected character '$'.");
return Verifier.Verify(result.Output);
}
[CommandOption("<FOO> $ <BAR>")]
public string Foo { get; set; }
}
[UsesVerify]
public sealed class UnterminatedValueName
[Fact]
[Expectation("UnexpectedCharacter")]
public Task Should_Return_Correct_Text()
{
public sealed class Settings : CommandSettings
{
[CommandOption("--foo|-f <BAR")]
public string Foo { get; set; }
}
// Given, When
var result = Fixture.Run<Settings>();
[Fact]
[Expectation("UnterminatedValueName")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Encountered unterminated value name 'BAR'.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class OptionsMustHaveName
{
public sealed class Settings : CommandSettings
{
[CommandOption("--foo|-")]
public string Foo { get; set; }
}
[Fact]
[Expectation("OptionsMustHaveName")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Options without name are not allowed.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class OptionNamesCannotStartWithDigit
{
public sealed class Settings : CommandSettings
{
[CommandOption("--1foo")]
public string Foo { get; set; }
}
[Fact]
[Expectation("OptionNamesCannotStartWithDigit")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Option names cannot start with a digit.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class InvalidCharacterInOptionName
{
public sealed class Settings : CommandSettings
{
[CommandOption("--f$oo")]
public string Foo { get; set; }
}
[Fact]
[Expectation("InvalidCharacterInOptionName")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Encountered invalid character '$' in option name.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class LongOptionMustHaveMoreThanOneCharacter
{
public sealed class Settings : CommandSettings
{
[CommandOption("--f")]
public string Foo { get; set; }
}
[Fact]
[Expectation("LongOptionMustHaveMoreThanOneCharacter")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Long option names must consist of more than one character.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class ShortOptionMustOnlyBeOneCharacter
{
public sealed class Settings : CommandSettings
{
[CommandOption("--foo|-bar")]
public string Foo { get; set; }
}
[Fact]
[Expectation("ShortOptionMustOnlyBeOneCharacter")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Short option names can not be longer than one character.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class MultipleOptionValuesAreNotSupported
{
public sealed class Settings : CommandSettings
{
[CommandOption("-f|--foo <FOO> <BAR>")]
public string Foo { get; set; }
}
[Fact]
[Expectation("MultipleOptionValuesAreNotSupported")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Multiple option values are not supported.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class InvalidCharacterInValueName
{
public sealed class Settings : CommandSettings
{
[CommandOption("-f|--foo <F$OO>")]
public string Foo { get; set; }
}
[Fact]
[Expectation("InvalidCharacterInValueName")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Encountered invalid character '$' in value name.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class MissingLongAndShortName
{
public sealed class Settings : CommandSettings
{
[CommandOption("<FOO>")]
public string Foo { get; set; }
}
[Fact]
[Expectation("MissingLongAndShortName")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("No long or short name for option has been specified.");
return Verifier.Verify(result.Output);
}
}
private static class Fixture
{
public static CommandAppFailure Run<TSettings>(params string[] args)
where TSettings : CommandSettings
{
var app = new CommandAppTester();
app.Configure(c =>
{
c.AddCommand<GenericCommand<TSettings>>("foo");
});
return app.RunAndCatch<CommandTemplateException>(args);
}
// Then
result.Exception.Message.ShouldBe("Encountered unexpected character '$'.");
return Verifier.Verify(result.Output);
}
}
}
[UsesVerify]
public sealed class UnterminatedValueName
{
public sealed class Settings : CommandSettings
{
[CommandOption("--foo|-f <BAR")]
public string Foo { get; set; }
}
[Fact]
[Expectation("UnterminatedValueName")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Encountered unterminated value name 'BAR'.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class OptionsMustHaveName
{
public sealed class Settings : CommandSettings
{
[CommandOption("--foo|-")]
public string Foo { get; set; }
}
[Fact]
[Expectation("OptionsMustHaveName")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Options without name are not allowed.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class OptionNamesCannotStartWithDigit
{
public sealed class Settings : CommandSettings
{
[CommandOption("--1foo")]
public string Foo { get; set; }
}
[Fact]
[Expectation("OptionNamesCannotStartWithDigit")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Option names cannot start with a digit.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class InvalidCharacterInOptionName
{
public sealed class Settings : CommandSettings
{
[CommandOption("--f$oo")]
public string Foo { get; set; }
}
[Fact]
[Expectation("InvalidCharacterInOptionName")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Encountered invalid character '$' in option name.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class LongOptionMustHaveMoreThanOneCharacter
{
public sealed class Settings : CommandSettings
{
[CommandOption("--f")]
public string Foo { get; set; }
}
[Fact]
[Expectation("LongOptionMustHaveMoreThanOneCharacter")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Long option names must consist of more than one character.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class ShortOptionMustOnlyBeOneCharacter
{
public sealed class Settings : CommandSettings
{
[CommandOption("--foo|-bar")]
public string Foo { get; set; }
}
[Fact]
[Expectation("ShortOptionMustOnlyBeOneCharacter")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Short option names can not be longer than one character.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class MultipleOptionValuesAreNotSupported
{
public sealed class Settings : CommandSettings
{
[CommandOption("-f|--foo <FOO> <BAR>")]
public string Foo { get; set; }
}
[Fact]
[Expectation("MultipleOptionValuesAreNotSupported")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Multiple option values are not supported.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class InvalidCharacterInValueName
{
public sealed class Settings : CommandSettings
{
[CommandOption("-f|--foo <F$OO>")]
public string Foo { get; set; }
}
[Fact]
[Expectation("InvalidCharacterInValueName")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("Encountered invalid character '$' in value name.");
return Verifier.Verify(result.Output);
}
}
[UsesVerify]
public sealed class MissingLongAndShortName
{
public sealed class Settings : CommandSettings
{
[CommandOption("<FOO>")]
public string Foo { get; set; }
}
[Fact]
[Expectation("MissingLongAndShortName")]
public Task Should_Return_Correct_Text()
{
// Given, When
var result = Fixture.Run<Settings>();
// Then
result.Exception.Message.ShouldBe("No long or short name for option has been specified.");
return Verifier.Verify(result.Output);
}
}
private static class Fixture
{
public static CommandAppFailure Run<TSettings>(params string[] args)
where TSettings : CommandSettings
{
var app = new CommandAppTester();
app.Configure(c =>
{
c.AddCommand<GenericCommand<TSettings>>("foo");
});
return app.RunAndCatch<CommandTemplateException>(args);
}
}
}

View File

@ -1,217 +1,212 @@
using Shouldly;
using Spectre.Console.Cli;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli.Annotations;
namespace Spectre.Console.Tests.Unit.Cli.Annotations
public sealed partial class CommandOptionAttributeTests
{
public sealed partial class CommandOptionAttributeTests
[Fact]
public void Should_Parse_Short_Name_Correctly()
{
[Fact]
public void Should_Parse_Short_Name_Correctly()
{
// Given, When
var option = new CommandOptionAttribute("-o|--option <VALUE>");
// Given, When
var option = new CommandOptionAttribute("-o|--option <VALUE>");
// Then
option.ShortNames.ShouldContain("o");
}
[Fact]
public void Should_Parse_Long_Name_Correctly()
{
// Given, When
var option = new CommandOptionAttribute("-o|--option <VALUE>");
// Then
option.LongNames.ShouldContain("option");
}
[Theory]
[InlineData("<VALUE>")]
public void Should_Parse_Value_Correctly(string value)
{
// Given, When
var option = new CommandOptionAttribute($"-o|--option {value}");
// Then
option.ValueName.ShouldBe("VALUE");
}
[Fact]
public void Should_Parse_Only_Short_Name()
{
// Given, When
var option = new CommandOptionAttribute("-o");
// Then
option.ShortNames.ShouldContain("o");
}
[Fact]
public void Should_Parse_Only_Long_Name()
{
// Given, When
var option = new CommandOptionAttribute("--option");
// Then
option.LongNames.ShouldContain("option");
}
[Theory]
[InlineData("")]
[InlineData("<VALUE>")]
public void Should_Throw_If_Template_Is_Empty(string value)
{
// Given, When
var option = Record.Exception(() => new CommandOptionAttribute(value));
// Then
option.ShouldBeOfType<CommandTemplateException>().And(e =>
e.Message.ShouldBe("No long or short name for option has been specified."));
}
[Theory]
[InlineData("--bar|-foo")]
[InlineData("--bar|-f-b")]
public void Should_Throw_If_Short_Name_Is_Invalid(string value)
{
// Given, When
var option = Record.Exception(() => new CommandOptionAttribute(value));
// Then
option.ShouldBeOfType<CommandTemplateException>().And(e =>
e.Message.ShouldBe("Short option names can not be longer than one character."));
}
[Theory]
[InlineData("--o")]
public void Should_Throw_If_Long_Name_Is_Invalid(string value)
{
// Given, When
var option = Record.Exception(() => new CommandOptionAttribute(value));
// Then
option.ShouldBeOfType<CommandTemplateException>().And(e =>
e.Message.ShouldBe("Long option names must consist of more than one character."));
}
[Theory]
[InlineData("-")]
[InlineData("--")]
public void Should_Throw_If_Option_Have_No_Name(string template)
{
// Given, When
var option = Record.Exception(() => new CommandOptionAttribute(template));
// Then
option.ShouldBeOfType<CommandTemplateException>().And(e =>
e.Message.ShouldBe("Options without name are not allowed."));
}
[Theory]
[InlineData("--foo|-foo[b", '[')]
[InlineData("--foo|-f€b", '€')]
[InlineData("--foo|-foo@b", '@')]
public void Should_Throw_If_Option_Contains_Invalid_Name(string template, char invalid)
{
// Given, When
var result = Record.Exception(() => new CommandOptionAttribute(template));
// Then
result.ShouldBeOfType<CommandTemplateException>().And(e =>
{
e.Message.ShouldBe($"Encountered invalid character '{invalid}' in option name.");
e.Template.ShouldBe(template);
});
}
[Theory]
[InlineData("--foo <HELLO-WORLD>", "HELLO-WORLD")]
[InlineData("--foo <HELLO_WORLD>", "HELLO_WORLD")]
public void Should_Accept_Dash_And_Underscore_In_Value_Name(string template, string name)
{
// Given, When
var result = new CommandOptionAttribute(template);
// Then
result.ValueName.ShouldBe(name);
}
[Theory]
[InlineData("--foo|-1")]
public void Should_Throw_If_First_Letter_Of_An_Option_Name_Is_A_Digit(string template)
{
// Given, When
var result = Record.Exception(() => new CommandOptionAttribute(template));
// Then
result.ShouldBeOfType<CommandTemplateException>().And(e =>
{
e.Message.ShouldBe("Option names cannot start with a digit.");
e.Template.ShouldBe(template);
});
}
[Fact]
public void Multiple_Short_Options_Are_Supported()
{
// Given, When
var result = new CommandOptionAttribute("-f|-b");
// Then
result.ShortNames.Count.ShouldBe(2);
result.ShortNames.ShouldContain("f");
result.ShortNames.ShouldContain("b");
}
[Fact]
public void Multiple_Long_Options_Are_Supported()
{
// Given, When
var result = new CommandOptionAttribute("--foo|--bar");
// Then
result.LongNames.Count.ShouldBe(2);
result.LongNames.ShouldContain("foo");
result.LongNames.ShouldContain("bar");
}
[Theory]
[InlineData("-f|--foo <BAR>")]
[InlineData("--foo|-f <BAR>")]
[InlineData("<BAR> --foo|-f")]
[InlineData("<BAR> -f|--foo")]
[InlineData("-f <BAR> --foo")]
[InlineData("--foo <BAR> -f")]
public void Template_Parts_Can_Appear_In_Any_Order(string template)
{
// Given, When
var result = new CommandOptionAttribute(template);
// Then
result.LongNames.ShouldContain("foo");
result.ShortNames.ShouldContain("f");
result.ValueName.ShouldBe("BAR");
}
[Fact]
public void Is_Not_Hidden_From_Help_By_Default()
{
// Given, When
var result = new CommandOptionAttribute("--foo");
// Then
result.IsHidden.ShouldBeFalse();
}
[Fact]
public void Can_Indicate_That_It_Must_Be_Hidden_From_Help_Text()
{
// Given, When
var result = new CommandOptionAttribute("--foo") { IsHidden = true };
// Then
result.IsHidden.ShouldBeTrue();
}
// Then
option.ShortNames.ShouldContain("o");
}
}
[Fact]
public void Should_Parse_Long_Name_Correctly()
{
// Given, When
var option = new CommandOptionAttribute("-o|--option <VALUE>");
// Then
option.LongNames.ShouldContain("option");
}
[Theory]
[InlineData("<VALUE>")]
public void Should_Parse_Value_Correctly(string value)
{
// Given, When
var option = new CommandOptionAttribute($"-o|--option {value}");
// Then
option.ValueName.ShouldBe("VALUE");
}
[Fact]
public void Should_Parse_Only_Short_Name()
{
// Given, When
var option = new CommandOptionAttribute("-o");
// Then
option.ShortNames.ShouldContain("o");
}
[Fact]
public void Should_Parse_Only_Long_Name()
{
// Given, When
var option = new CommandOptionAttribute("--option");
// Then
option.LongNames.ShouldContain("option");
}
[Theory]
[InlineData("")]
[InlineData("<VALUE>")]
public void Should_Throw_If_Template_Is_Empty(string value)
{
// Given, When
var option = Record.Exception(() => new CommandOptionAttribute(value));
// Then
option.ShouldBeOfType<CommandTemplateException>().And(e =>
e.Message.ShouldBe("No long or short name for option has been specified."));
}
[Theory]
[InlineData("--bar|-foo")]
[InlineData("--bar|-f-b")]
public void Should_Throw_If_Short_Name_Is_Invalid(string value)
{
// Given, When
var option = Record.Exception(() => new CommandOptionAttribute(value));
// Then
option.ShouldBeOfType<CommandTemplateException>().And(e =>
e.Message.ShouldBe("Short option names can not be longer than one character."));
}
[Theory]
[InlineData("--o")]
public void Should_Throw_If_Long_Name_Is_Invalid(string value)
{
// Given, When
var option = Record.Exception(() => new CommandOptionAttribute(value));
// Then
option.ShouldBeOfType<CommandTemplateException>().And(e =>
e.Message.ShouldBe("Long option names must consist of more than one character."));
}
[Theory]
[InlineData("-")]
[InlineData("--")]
public void Should_Throw_If_Option_Have_No_Name(string template)
{
// Given, When
var option = Record.Exception(() => new CommandOptionAttribute(template));
// Then
option.ShouldBeOfType<CommandTemplateException>().And(e =>
e.Message.ShouldBe("Options without name are not allowed."));
}
[Theory]
[InlineData("--foo|-foo[b", '[')]
[InlineData("--foo|-f€b", '€')]
[InlineData("--foo|-foo@b", '@')]
public void Should_Throw_If_Option_Contains_Invalid_Name(string template, char invalid)
{
// Given, When
var result = Record.Exception(() => new CommandOptionAttribute(template));
// Then
result.ShouldBeOfType<CommandTemplateException>().And(e =>
{
e.Message.ShouldBe($"Encountered invalid character '{invalid}' in option name.");
e.Template.ShouldBe(template);
});
}
[Theory]
[InlineData("--foo <HELLO-WORLD>", "HELLO-WORLD")]
[InlineData("--foo <HELLO_WORLD>", "HELLO_WORLD")]
public void Should_Accept_Dash_And_Underscore_In_Value_Name(string template, string name)
{
// Given, When
var result = new CommandOptionAttribute(template);
// Then
result.ValueName.ShouldBe(name);
}
[Theory]
[InlineData("--foo|-1")]
public void Should_Throw_If_First_Letter_Of_An_Option_Name_Is_A_Digit(string template)
{
// Given, When
var result = Record.Exception(() => new CommandOptionAttribute(template));
// Then
result.ShouldBeOfType<CommandTemplateException>().And(e =>
{
e.Message.ShouldBe("Option names cannot start with a digit.");
e.Template.ShouldBe(template);
});
}
[Fact]
public void Multiple_Short_Options_Are_Supported()
{
// Given, When
var result = new CommandOptionAttribute("-f|-b");
// Then
result.ShortNames.Count.ShouldBe(2);
result.ShortNames.ShouldContain("f");
result.ShortNames.ShouldContain("b");
}
[Fact]
public void Multiple_Long_Options_Are_Supported()
{
// Given, When
var result = new CommandOptionAttribute("--foo|--bar");
// Then
result.LongNames.Count.ShouldBe(2);
result.LongNames.ShouldContain("foo");
result.LongNames.ShouldContain("bar");
}
[Theory]
[InlineData("-f|--foo <BAR>")]
[InlineData("--foo|-f <BAR>")]
[InlineData("<BAR> --foo|-f")]
[InlineData("<BAR> -f|--foo")]
[InlineData("-f <BAR> --foo")]
[InlineData("--foo <BAR> -f")]
public void Template_Parts_Can_Appear_In_Any_Order(string template)
{
// Given, When
var result = new CommandOptionAttribute(template);
// Then
result.LongNames.ShouldContain("foo");
result.ShortNames.ShouldContain("f");
result.ValueName.ShouldBe("BAR");
}
[Fact]
public void Is_Not_Hidden_From_Help_By_Default()
{
// Given, When
var result = new CommandOptionAttribute("--foo");
// Then
result.IsHidden.ShouldBeFalse();
}
[Fact]
public void Can_Indicate_That_It_Must_Be_Hidden_From_Help_Text()
{
// Given, When
var result = new CommandOptionAttribute("--foo") { IsHidden = true };
// Then
result.IsHidden.ShouldBeTrue();
}
}

View File

@ -1,110 +1,104 @@
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
public class NullableSettings : CommandSettings
{
public class NullableSettings : CommandSettings
public NullableSettings(bool? detailed, string[] extra)
{
public NullableSettings(bool? detailed, string[] extra)
{
Detailed = detailed;
Extra = extra;
}
[CommandOption("-d")]
public bool? Detailed { get; }
[CommandArgument(0, "[extra]")]
public string[] Extra { get; }
Detailed = detailed;
Extra = extra;
}
public class NullableWithInitSettings : CommandSettings
{
[CommandOption("-d")]
public bool? Detailed { get; init; }
[CommandOption("-d")]
public bool? Detailed { get; }
[CommandArgument(0, "[extra]")]
public string[] Extra { get; init; }
}
public class NullableCommand : Command<NullableSettings>
{
public override int Execute(CommandContext context, NullableSettings settings) => 0;
}
public class NullableWithInitCommand : Command<NullableWithInitSettings>
{
public override int Execute(CommandContext context, NullableWithInitSettings settings) => 0;
}
[Fact]
public void Should_Populate_Nullable_Objects_In_Settings()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
configurator.AddCommand<NullableCommand>("null");
});
// When
var result = fixture.Run("null");
// Then
result.Settings.ShouldBeOfType<NullableSettings>().And(settings =>
{
settings.Detailed.ShouldBeNull();
settings.Extra.ShouldBeNull();
});
}
[Fact]
public void Should_Populate_Nullable_Objects_With_Init_In_Settings()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
configurator.AddCommand<NullableWithInitCommand>("null");
});
// When
var result = fixture.Run("null");
// Then
result.Settings.ShouldBeOfType<NullableWithInitSettings>().And(settings =>
{
settings.Detailed.ShouldBeNull();
settings.Extra.ShouldBeNull();
});
}
[Fact]
public void Should_Populate_Regular_Settings()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
configurator.AddCommand<NullableCommand>("null");
});
// When
var result = fixture.Run("null", "-d", "true", "first-item");
// Then
result.Settings.ShouldBeOfType<NullableSettings>().And(settings =>
{
settings.Detailed.ShouldBe(true);
settings.Extra.ShouldBe(new[] { "first-item" });
});
}
[CommandArgument(0, "[extra]")]
public string[] Extra { get; }
}
}
public class NullableWithInitSettings : CommandSettings
{
[CommandOption("-d")]
public bool? Detailed { get; init; }
[CommandArgument(0, "[extra]")]
public string[] Extra { get; init; }
}
public class NullableCommand : Command<NullableSettings>
{
public override int Execute(CommandContext context, NullableSettings settings) => 0;
}
public class NullableWithInitCommand : Command<NullableWithInitSettings>
{
public override int Execute(CommandContext context, NullableWithInitSettings settings) => 0;
}
[Fact]
public void Should_Populate_Nullable_Objects_In_Settings()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
configurator.AddCommand<NullableCommand>("null");
});
// When
var result = fixture.Run("null");
// Then
result.Settings.ShouldBeOfType<NullableSettings>().And(settings =>
{
settings.Detailed.ShouldBeNull();
settings.Extra.ShouldBeNull();
});
}
[Fact]
public void Should_Populate_Nullable_Objects_With_Init_In_Settings()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
configurator.AddCommand<NullableWithInitCommand>("null");
});
// When
var result = fixture.Run("null");
// Then
result.Settings.ShouldBeOfType<NullableWithInitSettings>().And(settings =>
{
settings.Detailed.ShouldBeNull();
settings.Extra.ShouldBeNull();
});
}
[Fact]
public void Should_Populate_Regular_Settings()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
configurator.AddCommand<NullableCommand>("null");
});
// When
var result = fixture.Run("null", "-d", "true", "first-item");
// Then
result.Settings.ShouldBeOfType<NullableSettings>().And(settings =>
{
settings.Detailed.ShouldBe(true);
settings.Extra.ShouldBe(new[] { "first-item" });
});
}
}

View File

@ -1,100 +1,92 @@
using System;
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
public sealed class Exception_Handling
{
public sealed class Exception_Handling
[Fact]
public void Should_Not_Propagate_Runtime_Exceptions_If_Not_Explicitly_Told_To_Do_So()
{
[Fact]
public void Should_Not_Propagate_Runtime_Exceptions_If_Not_Explicitly_Told_To_Do_So()
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
config.AddBranch<AnimalSettings>("animal", animal =>
{
config.AddBranch<AnimalSettings>("animal", animal =>
{
animal.AddCommand<DogCommand>("dog");
animal.AddCommand<HorseCommand>("horse");
});
animal.AddCommand<DogCommand>("dog");
animal.AddCommand<HorseCommand>("horse");
});
});
// When
var result = app.Run(new[] { "animal", "4", "dog", "101", "--name", "Rufus" });
// When
var result = app.Run(new[] { "animal", "4", "dog", "101", "--name", "Rufus" });
// Then
result.ExitCode.ShouldBe(-1);
}
// Then
result.ExitCode.ShouldBe(-1);
}
[Fact]
public void Should_Not_Propagate_Exceptions_If_Not_Explicitly_Told_To_Do_So()
[Fact]
public void Should_Not_Propagate_Exceptions_If_Not_Explicitly_Told_To_Do_So()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.AddCommand<ThrowingCommand>("throw");
});
config.AddCommand<ThrowingCommand>("throw");
});
// When
var result = app.Run(new[] { "throw" });
// When
var result = app.Run(new[] { "throw" });
// Then
result.ExitCode.ShouldBe(-1);
}
// Then
result.ExitCode.ShouldBe(-1);
}
[Fact]
public void Should_Handle_Exceptions_If_ExceptionHandler_Is_Set_Using_Action()
[Fact]
public void Should_Handle_Exceptions_If_ExceptionHandler_Is_Set_Using_Action()
{
// Given
var exceptionHandled = false;
var app = new CommandAppTester();
app.Configure(config =>
{
// Given
var exceptionHandled = false;
var app = new CommandAppTester();
app.Configure(config =>
config.AddCommand<ThrowingCommand>("throw");
config.SetExceptionHandler(_ =>
{
config.AddCommand<ThrowingCommand>("throw");
config.SetExceptionHandler(_ =>
{
exceptionHandled = true;
});
exceptionHandled = true;
});
});
// When
var result = app.Run(new[] { "throw" });
// When
var result = app.Run(new[] { "throw" });
// Then
result.ExitCode.ShouldBe(-1);
exceptionHandled.ShouldBeTrue();
}
// Then
result.ExitCode.ShouldBe(-1);
exceptionHandled.ShouldBeTrue();
}
[Fact]
public void Should_Handle_Exceptions_If_ExceptionHandler_Is_Set_Using_Function()
[Fact]
public void Should_Handle_Exceptions_If_ExceptionHandler_Is_Set_Using_Function()
{
// Given
var exceptionHandled = false;
var app = new CommandAppTester();
app.Configure(config =>
{
// Given
var exceptionHandled = false;
var app = new CommandAppTester();
app.Configure(config =>
config.AddCommand<ThrowingCommand>("throw");
config.SetExceptionHandler(_ =>
{
config.AddCommand<ThrowingCommand>("throw");
config.SetExceptionHandler(_ =>
{
exceptionHandled = true;
return -99;
});
exceptionHandled = true;
return -99;
});
});
// When
var result = app.Run(new[] { "throw" });
// When
var result = app.Run(new[] { "throw" });
// Then
result.ExitCode.ShouldBe(-99);
exceptionHandled.ShouldBeTrue();
}
// Then
result.ExitCode.ShouldBe(-99);
exceptionHandled.ShouldBeTrue();
}
}
}
}

View File

@ -1,223 +1,214 @@
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
public sealed class FlagValues
{
public sealed class FlagValues
[SuppressMessage("Performance", "CA1812", Justification = "It's OK")]
private sealed class FlagSettings : CommandSettings
{
[SuppressMessage("Performance", "CA1812", Justification = "It's OK")]
private sealed class FlagSettings : CommandSettings
[CommandOption("--serve [PORT]")]
public FlagValue<int> Serve { get; set; }
}
[SuppressMessage("Performance", "CA1812", Justification = "It's OK")]
private sealed class FlagSettingsWithNullableValueType : CommandSettings
{
[CommandOption("--serve [PORT]")]
public FlagValue<int?> Serve { get; set; }
}
[SuppressMessage("Performance", "CA1812", Justification = "It's OK")]
private sealed class FlagSettingsWithOptionalOptionButNoFlagValue : CommandSettings
{
[CommandOption("--serve [PORT]")]
public int Serve { get; set; }
}
[SuppressMessage("Performance", "CA1812", Justification = "It's OK")]
private sealed class FlagSettingsWithDefaultValue : CommandSettings
{
[CommandOption("--serve [PORT]")]
[DefaultValue(987)]
public FlagValue<int> Serve { get; set; }
}
[Fact]
public void Should_Throw_If_Command_Option_Value_Is_Optional_But_Type_Is_Not_A_Flag_Value()
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
[CommandOption("--serve [PORT]")]
public FlagValue<int> Serve { get; set; }
}
config.PropagateExceptions();
config.AddCommand<GenericCommand<FlagSettingsWithOptionalOptionButNoFlagValue>>("foo");
});
[SuppressMessage("Performance", "CA1812", Justification = "It's OK")]
private sealed class FlagSettingsWithNullableValueType : CommandSettings
// When
var result = Record.Exception(() => app.Run(new[] { "foo", "--serve", "123" }));
// Then
result.ShouldBeOfType<CommandConfigurationException>().And(ex =>
{
[CommandOption("--serve [PORT]")]
public FlagValue<int?> Serve { get; set; }
}
ex.Message.ShouldBe("The option 'serve' has an optional value but does not implement IFlagValue.");
});
}
[SuppressMessage("Performance", "CA1812", Justification = "It's OK")]
private sealed class FlagSettingsWithOptionalOptionButNoFlagValue : CommandSettings
[Fact]
public void Should_Set_Flag_And_Value_If_Both_Were_Provided()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
[CommandOption("--serve [PORT]")]
public int Serve { get; set; }
}
config.PropagateExceptions();
config.AddCommand<GenericCommand<FlagSettings>>("foo");
});
[SuppressMessage("Performance", "CA1812", Justification = "It's OK")]
private sealed class FlagSettingsWithDefaultValue : CommandSettings
// When
var result = app.Run(new[] { "foo", "--serve", "123", });
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<FlagSettings>().And(flag =>
{
[CommandOption("--serve [PORT]")]
[DefaultValue(987)]
public FlagValue<int> Serve { get; set; }
}
flag.Serve.IsSet.ShouldBeTrue();
flag.Serve.Value.ShouldBe(123);
});
}
[Fact]
public void Should_Throw_If_Command_Option_Value_Is_Optional_But_Type_Is_Not_A_Flag_Value()
[Fact]
public void Should_Only_Set_Flag_If_No_Value_Was_Provided()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<GenericCommand<FlagSettingsWithOptionalOptionButNoFlagValue>>("foo");
});
config.PropagateExceptions();
config.AddCommand<GenericCommand<FlagSettings>>("foo");
});
// When
var result = Record.Exception(() => app.Run(new[] { "foo", "--serve", "123" }));
// When
var result = app.Run(new[] { "foo", "--serve" });
// Then
result.ShouldBeOfType<CommandConfigurationException>().And(ex =>
{
ex.Message.ShouldBe("The option 'serve' has an optional value but does not implement IFlagValue.");
});
}
[Fact]
public void Should_Set_Flag_And_Value_If_Both_Were_Provided()
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<FlagSettings>().And(flag =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<GenericCommand<FlagSettings>>("foo");
});
flag.Serve.IsSet.ShouldBeTrue();
flag.Serve.Value.ShouldBe(0);
});
}
// When
var result = app.Run(new[] { "foo", "--serve", "123", });
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<FlagSettings>().And(flag =>
{
flag.Serve.IsSet.ShouldBeTrue();
flag.Serve.Value.ShouldBe(123);
});
}
[Fact]
public void Should_Only_Set_Flag_If_No_Value_Was_Provided()
[Fact]
public void Should_Set_Value_To_Default_Value_If_None_Was_Explicitly_Set()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<GenericCommand<FlagSettings>>("foo");
});
config.PropagateExceptions();
config.AddCommand<GenericCommand<FlagSettingsWithDefaultValue>>("foo");
});
// When
var result = app.Run(new[] { "foo", "--serve" });
// When
var result = app.Run(new[] { "foo", "--serve" });
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<FlagSettings>().And(flag =>
{
flag.Serve.IsSet.ShouldBeTrue();
flag.Serve.Value.ShouldBe(0);
});
}
[Fact]
public void Should_Set_Value_To_Default_Value_If_None_Was_Explicitly_Set()
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<FlagSettingsWithDefaultValue>().And(flag =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<GenericCommand<FlagSettingsWithDefaultValue>>("foo");
});
flag.Serve.IsSet.ShouldBeTrue();
flag.Serve.Value.ShouldBe(987);
});
}
// When
var result = app.Run(new[] { "foo", "--serve" });
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<FlagSettingsWithDefaultValue>().And(flag =>
{
flag.Serve.IsSet.ShouldBeTrue();
flag.Serve.Value.ShouldBe(987);
});
}
[Fact]
public void Should_Create_Unset_Instance_If_Flag_Was_Not_Set()
[Fact]
public void Should_Create_Unset_Instance_If_Flag_Was_Not_Set()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<GenericCommand<FlagSettings>>("foo");
});
config.PropagateExceptions();
config.AddCommand<GenericCommand<FlagSettings>>("foo");
});
// When
var result = app.Run(new[] { "foo" });
// When
var result = app.Run(new[] { "foo" });
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<FlagSettings>().And(flag =>
{
flag.Serve.IsSet.ShouldBeFalse();
flag.Serve.Value.ShouldBe(0);
});
}
[Fact]
public void Should_Create_Unset_Instance_With_Null_For_Nullable_Value_Type_If_Flag_Was_Not_Set()
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<FlagSettings>().And(flag =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<GenericCommand<FlagSettingsWithNullableValueType>>("foo");
});
flag.Serve.IsSet.ShouldBeFalse();
flag.Serve.Value.ShouldBe(0);
});
}
// When
var result = app.Run(new[] { "foo" });
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<FlagSettingsWithNullableValueType>().And(flag =>
{
flag.Serve.IsSet.ShouldBeFalse();
flag.Serve.Value.ShouldBeNull();
});
}
[Theory]
[InlineData("Foo", true, "Set=True, Value=Foo")]
[InlineData("Bar", false, "Set=False, Value=Bar")]
public void Should_Return_Correct_String_Representation_From_ToString(
string value,
bool isSet,
string expected)
[Fact]
public void Should_Create_Unset_Instance_With_Null_For_Nullable_Value_Type_If_Flag_Was_Not_Set()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
// Given
var flag = new FlagValue<string>
{
Value = value,
IsSet = isSet,
};
config.PropagateExceptions();
config.AddCommand<GenericCommand<FlagSettingsWithNullableValueType>>("foo");
});
// When
var result = flag.ToString();
// When
var result = app.Run(new[] { "foo" });
// Then
result.ShouldBe(expected);
}
[Theory]
[InlineData(true, "Set=True")]
[InlineData(false, "Set=False")]
public void Should_Return_Correct_String_Representation_From_ToString_If_Value_Is_Not_Set(
bool isSet,
string expected)
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<FlagSettingsWithNullableValueType>().And(flag =>
{
// Given
var flag = new FlagValue<string>
{
IsSet = isSet,
};
flag.Serve.IsSet.ShouldBeFalse();
flag.Serve.Value.ShouldBeNull();
});
}
// When
var result = flag.ToString();
[Theory]
[InlineData("Foo", true, "Set=True, Value=Foo")]
[InlineData("Bar", false, "Set=False, Value=Bar")]
public void Should_Return_Correct_String_Representation_From_ToString(
string value,
bool isSet,
string expected)
{
// Given
var flag = new FlagValue<string>
{
Value = value,
IsSet = isSet,
};
// Then
result.ShouldBe(expected);
}
// When
var result = flag.ToString();
// Then
result.ShouldBe(expected);
}
[Theory]
[InlineData(true, "Set=True")]
[InlineData(false, "Set=False")]
public void Should_Return_Correct_String_Representation_From_ToString_If_Value_Is_Not_Set(
bool isSet,
string expected)
{
// Given
var flag = new FlagValue<string>
{
IsSet = isSet,
};
// When
var result = flag.ToString();
// Then
result.ShouldBe(expected);
}
}
}
}

View File

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

View File

@ -1,97 +1,90 @@
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
public sealed class Injection
{
public sealed class Injection
public sealed class FakeDependency
{
public sealed class FakeDependency
}
public sealed class InjectSettings : CommandSettings
{
public FakeDependency Fake { get; set; }
[CommandOption("--name <NAME>")]
public string Name { get; }
[CommandOption("--age <AGE>")]
public int Age { get; set; }
public InjectSettings(FakeDependency fake, string name)
{
}
public sealed class InjectSettings : CommandSettings
{
public FakeDependency Fake { get; set; }
[CommandOption("--name <NAME>")]
public string Name { get; }
[CommandOption("--age <AGE>")]
public int Age { get; set; }
public InjectSettings(FakeDependency fake, string name)
{
Fake = fake;
Name = "Hello " + name;
}
}
[Fact]
public void Should_Inject_Parameters()
{
// Given
var app = new CommandAppTester();
var dependency = new FakeDependency();
app.SetDefaultCommand<GenericCommand<InjectSettings>>();
app.Configure(config =>
{
config.Settings.Registrar.RegisterInstance(dependency);
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--name", "foo",
"--age", "35",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<InjectSettings>().And(injected =>
{
injected.ShouldNotBeNull();
injected.Fake.ShouldBeSameAs(dependency);
injected.Name.ShouldBe("Hello foo");
injected.Age.ShouldBe(35);
});
}
[Fact]
public void Should_Inject_Dependency_Using_A_Given_Registrar()
{
// Given
var dependency = new FakeDependency();
var registrar = new FakeTypeRegistrar { TypeResolverFactory = FakeTypeResolver.Factory };
registrar.RegisterInstance(typeof(FakeDependency), dependency);
var app = new CommandAppTester(registrar);
app.SetDefaultCommand<GenericCommand<InjectSettings>>();
app.Configure(config => config.PropagateExceptions());
// When
var result = app.Run(new[]
{
"--name", "foo",
"--age", "35",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<InjectSettings>().And(injected =>
{
injected.ShouldNotBeNull();
injected.Fake.ShouldBeSameAs(dependency);
injected.Name.ShouldBe("Hello foo");
injected.Age.ShouldBe(35);
});
Fake = fake;
Name = "Hello " + name;
}
}
[Fact]
public void Should_Inject_Parameters()
{
// Given
var app = new CommandAppTester();
var dependency = new FakeDependency();
app.SetDefaultCommand<GenericCommand<InjectSettings>>();
app.Configure(config =>
{
config.Settings.Registrar.RegisterInstance(dependency);
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--name", "foo",
"--age", "35",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<InjectSettings>().And(injected =>
{
injected.ShouldNotBeNull();
injected.Fake.ShouldBeSameAs(dependency);
injected.Name.ShouldBe("Hello foo");
injected.Age.ShouldBe(35);
});
}
[Fact]
public void Should_Inject_Dependency_Using_A_Given_Registrar()
{
// Given
var dependency = new FakeDependency();
var registrar = new FakeTypeRegistrar { TypeResolverFactory = FakeTypeResolver.Factory };
registrar.RegisterInstance(typeof(FakeDependency), dependency);
var app = new CommandAppTester(registrar);
app.SetDefaultCommand<GenericCommand<InjectSettings>>();
app.Configure(config => config.PropagateExceptions());
// When
var result = app.Run(new[]
{
"--name", "foo",
"--age", "35",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<InjectSettings>().And(injected =>
{
injected.ShouldNotBeNull();
injected.Fake.ShouldBeSameAs(dependency);
injected.Name.ShouldBe("Hello foo");
injected.Age.ShouldBe(35);
});
}
}
}
}

View File

@ -1,327 +1,316 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
public sealed class Pairs
{
public sealed class Pairs
public sealed class AmbiguousSettings : CommandSettings
{
public sealed class AmbiguousSettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
[PairDeconstructor(typeof(StringIntDeconstructor))]
[TypeConverter(typeof(CatAgilityConverter))]
public ILookup<string, string> Values { get; set; }
}
[CommandOption("--var <VALUE>")]
[PairDeconstructor(typeof(StringIntDeconstructor))]
[TypeConverter(typeof(CatAgilityConverter))]
public ILookup<string, string> Values { get; set; }
}
public sealed class NotDeconstructableSettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
[PairDeconstructor(typeof(StringIntDeconstructor))]
public string Values { get; set; }
}
public sealed class NotDeconstructableSettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
[PairDeconstructor(typeof(StringIntDeconstructor))]
public string Values { get; set; }
}
public sealed class DefaultPairDeconstructorSettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
public IDictionary<string, int> Values { get; set; }
}
public sealed class DefaultPairDeconstructorSettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
public IDictionary<string, int> Values { get; set; }
}
public sealed class DefaultPairDeconstructorEnumValueSettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
public IDictionary<string, DayOfWeek> Values { get; set; }
}
public sealed class DefaultPairDeconstructorEnumValueSettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
public IDictionary<string, DayOfWeek> Values { get; set; }
}
public sealed class LookupSettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
[PairDeconstructor(typeof(StringIntDeconstructor))]
public ILookup<string, string> Values { get; set; }
}
public sealed class LookupSettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
[PairDeconstructor(typeof(StringIntDeconstructor))]
public ILookup<string, string> Values { get; set; }
}
public sealed class DictionarySettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
[PairDeconstructor(typeof(StringIntDeconstructor))]
public IDictionary<string, string> Values { get; set; }
}
public sealed class DictionarySettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
[PairDeconstructor(typeof(StringIntDeconstructor))]
public IDictionary<string, string> Values { get; set; }
}
public sealed class ReadOnlyDictionarySettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
[PairDeconstructor(typeof(StringIntDeconstructor))]
public IReadOnlyDictionary<string, string> Values { get; set; }
}
public sealed class ReadOnlyDictionarySettings : CommandSettings
{
[CommandOption("--var <VALUE>")]
[PairDeconstructor(typeof(StringIntDeconstructor))]
public IReadOnlyDictionary<string, string> Values { get; set; }
}
public sealed class StringIntDeconstructor : PairDeconstructor<string, string>
public sealed class StringIntDeconstructor : PairDeconstructor<string, string>
{
protected override (string Key, string Value) Deconstruct(string value)
{
protected override (string Key, string Value) Deconstruct(string value)
if (value == null)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
var parts = value.Split(new[] { '=' });
if (parts.Length != 2)
{
throw new InvalidOperationException("Could not parse pair");
}
return (parts[0], parts[1]);
throw new ArgumentNullException(nameof(value));
}
}
[Fact]
public void Should_Throw_If_Option_Has_Pair_Deconstructor_And_Type_Converter()
{
// Given
var app = new CommandApp<GenericCommand<AmbiguousSettings>>();
app.Configure(config =>
var parts = value.Split(new[] { '=' });
if (parts.Length != 2)
{
config.PropagateExceptions();
});
throw new InvalidOperationException("Could not parse pair");
}
// When
var result = Record.Exception(() => app.Run(new[]
{
"--var", "foo=bar",
"--var", "foo=qux",
}));
// Then
result.ShouldBeOfType<CommandConfigurationException>().And(ex =>
{
ex.Message.ShouldBe("The option 'var' is both marked as pair deconstructable and convertable.");
});
}
[Fact]
public void Should_Throw_If_Option_Has_Pair_Deconstructor_But_Type_Is_Not_Deconstructable()
{
// Given
var app = new CommandApp<GenericCommand<NotDeconstructableSettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = Record.Exception(() => app.Run(new[]
{
"--var", "foo=bar",
"--var", "foo=qux",
}));
// Then
result.ShouldBeOfType<CommandConfigurationException>().And(ex =>
{
ex.Message.ShouldBe("The option 'var' is marked as pair deconstructable, but the underlying type does not support that.");
});
}
[Fact]
public void Should_Map_Pairs_To_Pair_Deconstructable_Collection_Using_Default_Deconstructort()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<DefaultPairDeconstructorSettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--var", "foo=1",
"--var", "foo=3",
"--var", "bar=4",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DefaultPairDeconstructorSettings>().And(pair =>
{
pair.Values.ShouldNotBeNull();
pair.Values.Count.ShouldBe(2);
pair.Values["foo"].ShouldBe(3);
pair.Values["bar"].ShouldBe(4);
});
}
[Fact]
public void Should_Map_Pairs_With_Enum_Value_To_Pair_Deconstructable_Collection_Using_Default_Deconstructor()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<DefaultPairDeconstructorEnumValueSettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--var", "foo=Monday",
"--var", "bar=Tuesday",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DefaultPairDeconstructorEnumValueSettings>().And(pair =>
{
pair.Values.ShouldNotBeNull();
pair.Values.Count.ShouldBe(2);
pair.Values["foo"].ShouldBe(DayOfWeek.Monday);
pair.Values["bar"].ShouldBe(DayOfWeek.Tuesday);
});
}
[Theory]
[InlineData("foo=1=2", "Error: The value 'foo=1=2' is not in a correct format")]
[InlineData("foo=1=2=3", "Error: The value 'foo=1=2=3' is not in a correct format")]
public void Should_Throw_If_Value_Is_Not_In_A_Valid_Format_Using_Default_Deconstructor(
string input, string expected)
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<DefaultPairDeconstructorSettings>>();
// When
var result = app.Run(new[]
{
"--var", input,
});
// Then
result.ExitCode.ShouldBe(-1);
result.Output.ShouldBe(expected);
}
[Fact]
public void Should_Map_Lookup_Values()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<LookupSettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--var", "foo=bar",
"--var", "foo=qux",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<LookupSettings>().And(pair =>
{
pair.Values.ShouldNotBeNull();
pair.Values.Count.ShouldBe(1);
pair.Values["foo"].ToList().Count.ShouldBe(2);
});
}
[Fact]
public void Should_Map_Dictionary_Values()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<DictionarySettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--var", "foo=bar",
"--var", "baz=qux",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DictionarySettings>().And(pair =>
{
pair.Values.ShouldNotBeNull();
pair.Values.Count.ShouldBe(2);
pair.Values["foo"].ShouldBe("bar");
pair.Values["baz"].ShouldBe("qux");
});
}
[Fact]
public void Should_Map_Latest_Value_Of_Same_Key_When_Mapping_To_Dictionary()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<DictionarySettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--var", "foo=bar",
"--var", "foo=qux",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DictionarySettings>().And(pair =>
{
pair.Values.ShouldNotBeNull();
pair.Values.Count.ShouldBe(1);
pair.Values["foo"].ShouldBe("qux");
});
}
[Fact]
public void Should_Map_ReadOnly_Dictionary_Values()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<ReadOnlyDictionarySettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--var", "foo=bar",
"--var", "baz=qux",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<ReadOnlyDictionarySettings>().And(pair =>
{
pair.Values.ShouldNotBeNull();
pair.Values.Count.ShouldBe(2);
pair.Values["foo"].ShouldBe("bar");
pair.Values["baz"].ShouldBe("qux");
});
return (parts[0], parts[1]);
}
}
[Fact]
public void Should_Throw_If_Option_Has_Pair_Deconstructor_And_Type_Converter()
{
// Given
var app = new CommandApp<GenericCommand<AmbiguousSettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = Record.Exception(() => app.Run(new[]
{
"--var", "foo=bar",
"--var", "foo=qux",
}));
// Then
result.ShouldBeOfType<CommandConfigurationException>().And(ex =>
{
ex.Message.ShouldBe("The option 'var' is both marked as pair deconstructable and convertable.");
});
}
[Fact]
public void Should_Throw_If_Option_Has_Pair_Deconstructor_But_Type_Is_Not_Deconstructable()
{
// Given
var app = new CommandApp<GenericCommand<NotDeconstructableSettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = Record.Exception(() => app.Run(new[]
{
"--var", "foo=bar",
"--var", "foo=qux",
}));
// Then
result.ShouldBeOfType<CommandConfigurationException>().And(ex =>
{
ex.Message.ShouldBe("The option 'var' is marked as pair deconstructable, but the underlying type does not support that.");
});
}
[Fact]
public void Should_Map_Pairs_To_Pair_Deconstructable_Collection_Using_Default_Deconstructort()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<DefaultPairDeconstructorSettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--var", "foo=1",
"--var", "foo=3",
"--var", "bar=4",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DefaultPairDeconstructorSettings>().And(pair =>
{
pair.Values.ShouldNotBeNull();
pair.Values.Count.ShouldBe(2);
pair.Values["foo"].ShouldBe(3);
pair.Values["bar"].ShouldBe(4);
});
}
[Fact]
public void Should_Map_Pairs_With_Enum_Value_To_Pair_Deconstructable_Collection_Using_Default_Deconstructor()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<DefaultPairDeconstructorEnumValueSettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--var", "foo=Monday",
"--var", "bar=Tuesday",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DefaultPairDeconstructorEnumValueSettings>().And(pair =>
{
pair.Values.ShouldNotBeNull();
pair.Values.Count.ShouldBe(2);
pair.Values["foo"].ShouldBe(DayOfWeek.Monday);
pair.Values["bar"].ShouldBe(DayOfWeek.Tuesday);
});
}
[Theory]
[InlineData("foo=1=2", "Error: The value 'foo=1=2' is not in a correct format")]
[InlineData("foo=1=2=3", "Error: The value 'foo=1=2=3' is not in a correct format")]
public void Should_Throw_If_Value_Is_Not_In_A_Valid_Format_Using_Default_Deconstructor(
string input, string expected)
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<DefaultPairDeconstructorSettings>>();
// When
var result = app.Run(new[]
{
"--var", input,
});
// Then
result.ExitCode.ShouldBe(-1);
result.Output.ShouldBe(expected);
}
[Fact]
public void Should_Map_Lookup_Values()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<LookupSettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--var", "foo=bar",
"--var", "foo=qux",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<LookupSettings>().And(pair =>
{
pair.Values.ShouldNotBeNull();
pair.Values.Count.ShouldBe(1);
pair.Values["foo"].ToList().Count.ShouldBe(2);
});
}
[Fact]
public void Should_Map_Dictionary_Values()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<DictionarySettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--var", "foo=bar",
"--var", "baz=qux",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DictionarySettings>().And(pair =>
{
pair.Values.ShouldNotBeNull();
pair.Values.Count.ShouldBe(2);
pair.Values["foo"].ShouldBe("bar");
pair.Values["baz"].ShouldBe("qux");
});
}
[Fact]
public void Should_Map_Latest_Value_Of_Same_Key_When_Mapping_To_Dictionary()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<DictionarySettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--var", "foo=bar",
"--var", "foo=qux",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DictionarySettings>().And(pair =>
{
pair.Values.ShouldNotBeNull();
pair.Values.Count.ShouldBe(1);
pair.Values["foo"].ShouldBe("qux");
});
}
[Fact]
public void Should_Map_ReadOnly_Dictionary_Values()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<ReadOnlyDictionarySettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
// When
var result = app.Run(new[]
{
"--var", "foo=bar",
"--var", "baz=qux",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<ReadOnlyDictionarySettings>().And(pair =>
{
pair.Values.ShouldNotBeNull();
pair.Values.Count.ShouldBe(2);
pair.Values["foo"].ShouldBe("bar");
pair.Values["baz"].ShouldBe("qux");
});
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,77 +1,70 @@
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
public sealed class Remaining
{
public sealed class Remaining
[Fact]
public void Should_Register_Remaining_Parsed_Arguments_With_Context()
{
[Fact]
public void Should_Register_Remaining_Parsed_Arguments_With_Context()
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
{
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
{
animal.AddCommand<DogCommand>("dog");
});
animal.AddCommand<DogCommand>("dog");
});
});
// When
var result = app.Run(new[]
{
"animal", "4", "dog", "12", "--",
"--foo", "bar", "--foo", "baz",
"-bar", "\"baz\"", "qux",
"foo bar baz qux",
});
// Then
result.Context.Remaining.Parsed.Count.ShouldBe(4);
result.Context.ShouldHaveRemainingArgument("foo", values: new[] { "bar", "baz" });
result.Context.ShouldHaveRemainingArgument("b", values: new[] { (string)null });
result.Context.ShouldHaveRemainingArgument("a", values: new[] { (string)null });
result.Context.ShouldHaveRemainingArgument("r", values: new[] { (string)null });
}
[Fact]
public void Should_Register_Remaining_Raw_Arguments_With_Context()
// When
var result = app.Run(new[]
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
{
animal.AddCommand<DogCommand>("dog");
});
});
"animal", "4", "dog", "12", "--",
"--foo", "bar", "--foo", "baz",
"-bar", "\"baz\"", "qux",
"foo bar baz qux",
});
// When
var result = app.Run(new[]
{
"animal", "4", "dog", "12", "--",
"--foo", "bar", "-bar", "\"baz\"", "qux",
"foo bar baz qux",
});
// Then
result.Context.Remaining.Parsed.Count.ShouldBe(4);
result.Context.ShouldHaveRemainingArgument("foo", values: new[] { "bar", "baz" });
result.Context.ShouldHaveRemainingArgument("b", values: new[] { (string)null });
result.Context.ShouldHaveRemainingArgument("a", values: new[] { (string)null });
result.Context.ShouldHaveRemainingArgument("r", values: new[] { (string)null });
}
// Then
result.Context.Remaining.Raw.Count.ShouldBe(6);
result.Context.Remaining.Raw[0].ShouldBe("--foo");
result.Context.Remaining.Raw[1].ShouldBe("bar");
result.Context.Remaining.Raw[2].ShouldBe("-bar");
result.Context.Remaining.Raw[3].ShouldBe("baz");
result.Context.Remaining.Raw[4].ShouldBe("qux");
result.Context.Remaining.Raw[5].ShouldBe("foo bar baz qux");
}
[Fact]
public void Should_Register_Remaining_Raw_Arguments_With_Context()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
{
animal.AddCommand<DogCommand>("dog");
});
});
// When
var result = app.Run(new[]
{
"animal", "4", "dog", "12", "--",
"--foo", "bar", "-bar", "\"baz\"", "qux",
"foo bar baz qux",
});
// Then
result.Context.Remaining.Raw.Count.ShouldBe(6);
result.Context.Remaining.Raw[0].ShouldBe("--foo");
result.Context.Remaining.Raw[1].ShouldBe("bar");
result.Context.Remaining.Raw[2].ShouldBe("-bar");
result.Context.Remaining.Raw[3].ShouldBe("baz");
result.Context.Remaining.Raw[4].ShouldBe("qux");
result.Context.Remaining.Raw[5].ShouldBe("foo bar baz qux");
}
}
}
}

View File

@ -1,118 +1,111 @@
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandApptests
{
public sealed partial class CommandApptests
[Fact]
public void Should_Treat_Commands_As_Case_Sensitive_If_Specified()
{
[Fact]
public void Should_Treat_Commands_As_Case_Sensitive_If_Specified()
// Given
var app = new CommandApp();
app.Configure(config =>
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
config.UseStrictParsing();
config.PropagateExceptions();
config.CaseSensitivity(CaseSensitivity.Commands);
config.AddCommand<GenericCommand<StringOptionSettings>>("command");
});
config.UseStrictParsing();
config.PropagateExceptions();
config.CaseSensitivity(CaseSensitivity.Commands);
config.AddCommand<GenericCommand<StringOptionSettings>>("command");
});
// When
var result = Record.Exception(() => app.Run(new[]
{
"Command", "--foo", "bar",
}));
// Then
result.ShouldNotBeNull();
result.ShouldBeOfType<CommandParseException>().And(ex =>
{
ex.Message.ShouldBe("Unknown command 'Command'.");
});
}
[Fact]
public void Should_Treat_Long_Options_As_Case_Sensitive_If_Specified()
// When
var result = Record.Exception(() => app.Run(new[]
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
config.UseStrictParsing();
config.PropagateExceptions();
config.CaseSensitivity(CaseSensitivity.LongOptions);
config.AddCommand<GenericCommand<StringOptionSettings>>("command");
});
"Command", "--foo", "bar",
}));
// When
var result = Record.Exception(() => app.Run(new[]
{
"command", "--Foo", "bar",
}));
// Then
result.ShouldNotBeNull();
result.ShouldBeOfType<CommandParseException>().And(ex =>
{
ex.Message.ShouldBe("Unknown option 'Foo'.");
});
}
[Fact]
public void Should_Treat_Short_Options_As_Case_Sensitive()
// Then
result.ShouldNotBeNull();
result.ShouldBeOfType<CommandParseException>().And(ex =>
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
config.UseStrictParsing();
config.PropagateExceptions();
config.AddCommand<GenericCommand<StringOptionSettings>>("command");
});
// When
var result = Record.Exception(() => app.Run(new[]
{
"command", "-F", "bar",
}));
// Then
result.ShouldNotBeNull();
result.ShouldBeOfType<CommandParseException>().And(ex =>
{
ex.Message.ShouldBe("Unknown option 'F'.");
});
}
[Fact]
public void Should_Suppress_Case_Sensitivity_If_Specified()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.UseStrictParsing();
config.PropagateExceptions();
config.CaseSensitivity(CaseSensitivity.None);
config.AddCommand<GenericCommand<StringOptionSettings>>("command");
});
// When
var result = app.Run(new[]
{
"Command", "--Foo", "bar",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<StringOptionSettings>().And(vec =>
{
vec.Foo.ShouldBe("bar");
});
}
ex.Message.ShouldBe("Unknown command 'Command'.");
});
}
}
[Fact]
public void Should_Treat_Long_Options_As_Case_Sensitive_If_Specified()
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
config.UseStrictParsing();
config.PropagateExceptions();
config.CaseSensitivity(CaseSensitivity.LongOptions);
config.AddCommand<GenericCommand<StringOptionSettings>>("command");
});
// When
var result = Record.Exception(() => app.Run(new[]
{
"command", "--Foo", "bar",
}));
// Then
result.ShouldNotBeNull();
result.ShouldBeOfType<CommandParseException>().And(ex =>
{
ex.Message.ShouldBe("Unknown option 'Foo'.");
});
}
[Fact]
public void Should_Treat_Short_Options_As_Case_Sensitive()
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
config.UseStrictParsing();
config.PropagateExceptions();
config.AddCommand<GenericCommand<StringOptionSettings>>("command");
});
// When
var result = Record.Exception(() => app.Run(new[]
{
"command", "-F", "bar",
}));
// Then
result.ShouldNotBeNull();
result.ShouldBeOfType<CommandParseException>().And(ex =>
{
ex.Message.ShouldBe("Unknown option 'F'.");
});
}
[Fact]
public void Should_Suppress_Case_Sensitivity_If_Specified()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.UseStrictParsing();
config.PropagateExceptions();
config.CaseSensitivity(CaseSensitivity.None);
config.AddCommand<GenericCommand<StringOptionSettings>>("command");
});
// When
var result = app.Run(new[]
{
"Command", "--Foo", "bar",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<StringOptionSettings>().And(vec =>
{
vec.Foo.ShouldBe("bar");
});
}
}

View File

@ -1,26 +1,21 @@
using Shouldly;
using Spectre.Console.Cli;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
[Fact]
public void Should_Apply_Case_Sensitivity_For_Everything_By_Default()
{
[Fact]
public void Should_Apply_Case_Sensitivity_For_Everything_By_Default()
// Given
var app = new CommandApp();
// When
var defaultSensitivity = CaseSensitivity.None;
app.Configure(config =>
{
// Given
var app = new CommandApp();
defaultSensitivity = config.Settings.CaseSensitivity;
});
// When
var defaultSensitivity = CaseSensitivity.None;
app.Configure(config =>
{
defaultSensitivity = config.Settings.CaseSensitivity;
});
// Then
defaultSensitivity.ShouldBe(CaseSensitivity.All);
}
// Then
defaultSensitivity.ShouldBe(CaseSensitivity.All);
}
}
}

View File

@ -1,40 +1,33 @@
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
public sealed class TypeConverters
{
public sealed class TypeConverters
[Fact]
public void Should_Bind_Using_Custom_Type_Converter_If_Specified()
{
[Fact]
public void Should_Bind_Using_Custom_Type_Converter_If_Specified()
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<CatCommand>("cat");
});
config.PropagateExceptions();
config.AddCommand<CatCommand>("cat");
});
// When
var result = app.Run(new[]
{
"cat", "--name", "Tiger",
"--agility", "FOOBAR",
});
// When
var result = app.Run(new[]
{
"cat", "--name", "Tiger",
"--agility", "FOOBAR",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<CatSettings>().And(cat =>
{
cat.Agility.ShouldBe(6);
});
}
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<CatSettings>().And(cat =>
{
cat.Agility.ShouldBe(6);
});
}
}
}
}

View File

@ -1,299 +1,291 @@
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Cli.Unsafe;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
public sealed class SafetyOff
{
public sealed class SafetyOff
[Fact]
public void Can_Mix_Safe_And_Unsafe_Configurators()
{
[Fact]
public void Can_Mix_Safe_And_Unsafe_Configurators()
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
config.AddBranch<AnimalSettings>("animal", animal =>
{
animal.SafetyOff().AddBranch("mammal", typeof(MammalSettings), mammal =>
{
animal.SafetyOff().AddBranch("mammal", typeof(MammalSettings), mammal =>
{
mammal.AddCommand("dog", typeof(DogCommand));
mammal.AddCommand("horse", typeof(HorseCommand));
});
mammal.AddCommand("dog", typeof(DogCommand));
mammal.AddCommand("horse", typeof(HorseCommand));
});
});
});
// When
var result = app.Run(new[]
{
"animal", "--alive", "mammal", "--name",
"Rufus", "dog", "12", "--good-boy",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus");
dog.IsAlive.ShouldBe(true);
});
}
[Fact]
public void Can_Turn_Safety_On_After_Turning_It_Off_For_Branch()
// When
var result = app.Run(new[]
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
"animal", "--alive", "mammal", "--name",
"Rufus", "dog", "12", "--good-boy",
});
config.SafetyOff().AddBranch("animal", typeof(AnimalSettings), animal =>
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus");
dog.IsAlive.ShouldBe(true);
});
}
[Fact]
public void Can_Turn_Safety_On_After_Turning_It_Off_For_Branch()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.SafetyOff().AddBranch("animal", typeof(AnimalSettings), animal =>
{
animal.SafetyOn<AnimalSettings>()
.AddBranch<MammalSettings>("mammal", mammal =>
{
animal.SafetyOn<AnimalSettings>()
.AddBranch<MammalSettings>("mammal", mammal =>
{
mammal.SafetyOff().AddCommand("dog", typeof(DogCommand));
mammal.AddCommand<HorseCommand>("horse");
});
mammal.SafetyOff().AddCommand("dog", typeof(DogCommand));
mammal.AddCommand<HorseCommand>("horse");
});
});
});
// When
var result = app.Run(new[]
{
"animal", "--alive", "mammal", "--name",
"Rufus", "dog", "12", "--good-boy",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus");
dog.IsAlive.ShouldBe(true);
});
}
[Fact]
public void Should_Throw_If_Trying_To_Convert_Unsafe_Branch_Configurator_To_Safe_Version_With_Wrong_Type()
// When
var result = app.Run(new[]
{
// Given
var app = new CommandApp();
"animal", "--alive", "mammal", "--name",
"Rufus", "dog", "12", "--good-boy",
});
// When
var result = Record.Exception(() => app.Configure(config =>
{
config.PropagateExceptions();
config.SafetyOff().AddBranch("animal", typeof(AnimalSettings), animal =>
{
animal.SafetyOn<MammalSettings>().AddCommand<DogCommand>("dog");
});
}));
// Then
result.ShouldBeOfType<CommandConfigurationException>();
result.Message.ShouldBe("Configurator cannot be converted to a safe configurator of type 'MammalSettings'.");
}
[Fact]
public void Should_Pass_Case_1()
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus");
dog.IsAlive.ShouldBe(true);
});
}
config.SafetyOff().AddBranch("animal", typeof(AnimalSettings), animal =>
[Fact]
public void Should_Throw_If_Trying_To_Convert_Unsafe_Branch_Configurator_To_Safe_Version_With_Wrong_Type()
{
// Given
var app = new CommandApp();
// When
var result = Record.Exception(() => app.Configure(config =>
{
config.PropagateExceptions();
config.SafetyOff().AddBranch("animal", typeof(AnimalSettings), animal =>
{
animal.SafetyOn<MammalSettings>().AddCommand<DogCommand>("dog");
});
}));
// Then
result.ShouldBeOfType<CommandConfigurationException>();
result.Message.ShouldBe("Configurator cannot be converted to a safe configurator of type 'MammalSettings'.");
}
[Fact]
public void Should_Pass_Case_1()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.SafetyOff().AddBranch("animal", typeof(AnimalSettings), animal =>
{
animal.AddBranch("mammal", typeof(MammalSettings), mammal =>
{
animal.AddBranch("mammal", typeof(MammalSettings), mammal =>
{
mammal.AddCommand("dog", typeof(DogCommand));
mammal.AddCommand("horse", typeof(HorseCommand));
});
mammal.AddCommand("dog", typeof(DogCommand));
mammal.AddCommand("horse", typeof(HorseCommand));
});
});
});
// When
var result = app.Run(new[]
{
"animal", "--alive", "mammal", "--name",
"Rufus", "dog", "12", "--good-boy",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus");
dog.IsAlive.ShouldBe(true);
});
}
[Fact]
public void Should_Pass_Case_2()
// When
var result = app.Run(new[]
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.SafetyOff().AddCommand("dog", typeof(DogCommand));
});
"animal", "--alive", "mammal", "--name",
"Rufus", "dog", "12", "--good-boy",
});
// When
var result = app.Run(new[]
{
"dog", "12", "4", "--good-boy",
"--name", "Rufus", "--alive",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.Legs.ShouldBe(12);
dog.Age.ShouldBe(4);
dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus");
dog.IsAlive.ShouldBe(true);
});
}
[Fact]
public void Should_Pass_Case_3()
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.SafetyOff().AddBranch("animal", typeof(AnimalSettings), animal =>
{
animal.AddCommand("dog", typeof(DogCommand));
animal.AddCommand("horse", typeof(HorseCommand));
});
});
dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus");
dog.IsAlive.ShouldBe(true);
});
}
// When
var result = app.Run(new[]
{
"animal", "dog", "12", "--good-boy",
"--name", "Rufus",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus");
dog.IsAlive.ShouldBe(false);
});
}
[Fact]
public void Should_Pass_Case_4()
[Fact]
public void Should_Pass_Case_2()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.SafetyOff().AddBranch("animal", typeof(AnimalSettings), animal =>
{
animal.AddCommand("dog", typeof(DogCommand));
});
});
config.PropagateExceptions();
config.SafetyOff().AddCommand("dog", typeof(DogCommand));
});
// When
var result = app.Run(new[]
{
"animal", "4", "dog", "12",
"--good-boy", "--name", "Rufus",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.Legs.ShouldBe(4);
dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true);
dog.IsAlive.ShouldBe(false);
dog.Name.ShouldBe("Rufus");
});
}
[Fact]
public void Should_Pass_Case_5()
// When
var result = app.Run(new[]
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.SafetyOff().AddCommand("multi", typeof(OptionVectorCommand));
});
"dog", "12", "4", "--good-boy",
"--name", "Rufus", "--alive",
});
// When
var result = app.Run(new[]
{
"multi", "--foo", "a", "--foo", "b", "--bar", "1", "--foo", "c", "--bar", "2",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<OptionVectorSettings>().And(vec =>
{
vec.Foo.Length.ShouldBe(3);
vec.Foo.ShouldBe(new[] { "a", "b", "c" });
vec.Bar.Length.ShouldBe(2);
vec.Bar.ShouldBe(new[] { 1, 2 });
});
}
[Fact]
public void Should_Pass_Case_6()
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<GenericCommand<ArgumentVectorSettings>>("multi");
});
dog.Legs.ShouldBe(12);
dog.Age.ShouldBe(4);
dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus");
dog.IsAlive.ShouldBe(true);
});
}
// When
var result = app.Run(new[]
[Fact]
public void Should_Pass_Case_3()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.SafetyOff().AddBranch("animal", typeof(AnimalSettings), animal =>
{
"multi", "a", "b", "c",
animal.AddCommand("dog", typeof(DogCommand));
animal.AddCommand("horse", typeof(HorseCommand));
});
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<ArgumentVectorSettings>().And(vec =>
// When
var result = app.Run(new[]
{
"animal", "dog", "12", "--good-boy",
"--name", "Rufus",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus");
dog.IsAlive.ShouldBe(false);
});
}
[Fact]
public void Should_Pass_Case_4()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.SafetyOff().AddBranch("animal", typeof(AnimalSettings), animal =>
{
vec.Foo.Length.ShouldBe(3);
vec.Foo.ShouldBe(new[] { "a", "b", "c" });
animal.AddCommand("dog", typeof(DogCommand));
});
}
});
// When
var result = app.Run(new[]
{
"animal", "4", "dog", "12",
"--good-boy", "--name", "Rufus",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.Legs.ShouldBe(4);
dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true);
dog.IsAlive.ShouldBe(false);
dog.Name.ShouldBe("Rufus");
});
}
[Fact]
public void Should_Pass_Case_5()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.SafetyOff().AddCommand("multi", typeof(OptionVectorCommand));
});
// When
var result = app.Run(new[]
{
"multi", "--foo", "a", "--foo", "b", "--bar", "1", "--foo", "c", "--bar", "2",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<OptionVectorSettings>().And(vec =>
{
vec.Foo.Length.ShouldBe(3);
vec.Foo.ShouldBe(new[] { "a", "b", "c" });
vec.Bar.Length.ShouldBe(2);
vec.Bar.ShouldBe(new[] { 1, 2 });
});
}
[Fact]
public void Should_Pass_Case_6()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<GenericCommand<ArgumentVectorSettings>>("multi");
});
// When
var result = app.Run(new[]
{
"multi", "a", "b", "c",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<ArgumentVectorSettings>().And(vec =>
{
vec.Foo.Length.ShouldBe(3);
vec.Foo.ShouldBe(new[] { "a", "b", "c" });
});
}
}
}
}

View File

@ -1,88 +1,82 @@
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
public sealed class Validation
{
public sealed class Validation
[Fact]
public void Should_Throw_If_Attribute_Validation_Fails()
{
[Fact]
public void Should_Throw_If_Attribute_Validation_Fails()
// Given
var app = new CommandApp();
app.Configure(config =>
{
// Given
var app = new CommandApp();
app.Configure(config =>
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
{
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
{
animal.AddCommand<DogCommand>("dog");
animal.AddCommand<HorseCommand>("horse");
});
animal.AddCommand<DogCommand>("dog");
animal.AddCommand<HorseCommand>("horse");
});
});
// When
var result = Record.Exception(() => app.Run(new[] { "animal", "3", "dog", "7", "--name", "Rufus" }));
// When
var result = Record.Exception(() => app.Run(new[] { "animal", "3", "dog", "7", "--name", "Rufus" }));
// Then
result.ShouldBeOfType<CommandRuntimeException>().And(e =>
{
e.Message.ShouldBe("Animals must have an even number of legs.");
});
}
[Fact]
public void Should_Throw_If_Settings_Validation_Fails()
// Then
result.ShouldBeOfType<CommandRuntimeException>().And(e =>
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
{
animal.AddCommand<DogCommand>("dog");
animal.AddCommand<HorseCommand>("horse");
});
});
e.Message.ShouldBe("Animals must have an even number of legs.");
});
}
// When
var result = Record.Exception(() => app.Run(new[] { "animal", "4", "dog", "7", "--name", "Tiger" }));
// Then
result.ShouldBeOfType<CommandRuntimeException>().And(e =>
{
e.Message.ShouldBe("Tiger is not a dog name!");
});
}
[Fact]
public void Should_Throw_If_Command_Validation_Fails()
[Fact]
public void Should_Throw_If_Settings_Validation_Fails()
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
// Given
var app = new CommandApp();
app.Configure(config =>
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
{
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
{
animal.AddCommand<DogCommand>("dog");
animal.AddCommand<HorseCommand>("horse");
});
animal.AddCommand<DogCommand>("dog");
animal.AddCommand<HorseCommand>("horse");
});
});
// When
var result = Record.Exception(() => app.Run(new[] { "animal", "4", "dog", "101", "--name", "Rufus" }));
// When
var result = Record.Exception(() => app.Run(new[] { "animal", "4", "dog", "7", "--name", "Tiger" }));
// Then
result.ShouldBeOfType<CommandRuntimeException>().And(e =>
// Then
result.ShouldBeOfType<CommandRuntimeException>().And(e =>
{
e.Message.ShouldBe("Tiger is not a dog name!");
});
}
[Fact]
public void Should_Throw_If_Command_Validation_Fails()
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
{
e.Message.ShouldBe("Dog is too old...");
animal.AddCommand<DogCommand>("dog");
animal.AddCommand<HorseCommand>("horse");
});
}
});
// When
var result = Record.Exception(() => app.Run(new[] { "animal", "4", "dog", "101", "--name", "Rufus" }));
// Then
result.ShouldBeOfType<CommandRuntimeException>().And(e =>
{
e.Message.ShouldBe("Dog is too old...");
});
}
}
}
}

View File

@ -1,97 +1,88 @@
using System.ComponentModel;
using System.Globalization;
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
public sealed class ValueProviders
{
public sealed class ValueProviders
public sealed class ValueProviderSettings : CommandSettings
{
public sealed class ValueProviderSettings : CommandSettings
[CommandOption("-f|--foo <VALUE>")]
[IntegerValueProvider(32)]
[TypeConverter(typeof(HexConverter))]
public string Foo { get; set; }
}
public sealed class IntegerValueProvider : ParameterValueProviderAttribute
{
private readonly int _value;
public IntegerValueProvider(int value)
{
[CommandOption("-f|--foo <VALUE>")]
[IntegerValueProvider(32)]
[TypeConverter(typeof(HexConverter))]
public string Foo { get; set; }
_value = value;
}
public sealed class IntegerValueProvider : ParameterValueProviderAttribute
public override bool TryGetValue(CommandParameterContext context, out object result)
{
private readonly int _value;
public IntegerValueProvider(int value)
if (context.Value == null)
{
_value = value;
result = _value;
return true;
}
public override bool TryGetValue(CommandParameterContext context, out object result)
{
if (context.Value == null)
{
result = _value;
return true;
}
result = null;
return false;
}
}
public sealed class HexConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is int integer)
{
return integer.ToString("X");
}
return value is string stringValue && int.TryParse(stringValue, out var intValue)
? intValue.ToString("X")
: base.ConvertFrom(context, culture, value);
}
}
[Fact]
public void Should_Use_Provided_Value_If_No_Value_Was_Specified()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<ValueProviderSettings>>();
app.Configure(config => config.PropagateExceptions());
// When
var result = app.Run();
// Then
result.Settings.ShouldBeOfType<ValueProviderSettings>().And(settings =>
{
settings.Foo.ShouldBe("20"); // 32 is 0x20
});
}
[Fact]
public void Should_Not_Override_Value_If_Value_Was_Specified()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<ValueProviderSettings>>();
app.Configure(config => config.PropagateExceptions());
// When
var result = app.Run("--foo", "12");
// Then
result.Settings.ShouldBeOfType<ValueProviderSettings>().And(settings =>
{
settings.Foo.ShouldBe("C"); // 12 is 0xC
});
result = null;
return false;
}
}
public sealed class HexConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is int integer)
{
return integer.ToString("X");
}
return value is string stringValue && int.TryParse(stringValue, out var intValue)
? intValue.ToString("X")
: base.ConvertFrom(context, culture, value);
}
}
[Fact]
public void Should_Use_Provided_Value_If_No_Value_Was_Specified()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<ValueProviderSettings>>();
app.Configure(config => config.PropagateExceptions());
// When
var result = app.Run();
// Then
result.Settings.ShouldBeOfType<ValueProviderSettings>().And(settings =>
{
settings.Foo.ShouldBe("20"); // 32 is 0x20
});
}
[Fact]
public void Should_Not_Override_Value_If_Value_Was_Specified()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<ValueProviderSettings>>();
app.Configure(config => config.PropagateExceptions());
// When
var result = app.Run("--foo", "12");
// Then
result.Settings.ShouldBeOfType<ValueProviderSettings>().And(settings =>
{
settings.Foo.ShouldBe("C"); // 12 is 0xC
});
}
}
}
}

View File

@ -1,111 +1,104 @@
using Shouldly;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
public sealed class Vectors
{
public sealed class Vectors
[Fact]
public void Should_Throw_If_A_Single_Command_Has_Multiple_Argument_Vectors()
{
[Fact]
public void Should_Throw_If_A_Single_Command_Has_Multiple_Argument_Vectors()
// Given
var app = new CommandApp();
app.Configure(config =>
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<GenericCommand<MultipleArgumentVectorSettings>>("multi");
});
config.PropagateExceptions();
config.AddCommand<GenericCommand<MultipleArgumentVectorSettings>>("multi");
});
// When
var result = Record.Exception(() => app.Run(new[] { "multi", "a", "b", "c" }));
// When
var result = Record.Exception(() => app.Run(new[] { "multi", "a", "b", "c" }));
// Then
result.ShouldBeOfType<CommandConfigurationException>().And(ex =>
{
ex.Message.ShouldBe("The command 'multi' specifies more than one vector argument.");
});
}
[Fact]
public void Should_Throw_If_An_Argument_Vector_Is_Not_Specified_Last()
// Then
result.ShouldBeOfType<CommandConfigurationException>().And(ex =>
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<GenericCommand<MultipleArgumentVectorSpecifiedFirstSettings>>("multi");
});
ex.Message.ShouldBe("The command 'multi' specifies more than one vector argument.");
});
}
// When
var result = Record.Exception(() => app.Run(new[] { "multi", "a", "b", "c" }));
// Then
result.ShouldBeOfType<CommandConfigurationException>().And(ex =>
{
ex.Message.ShouldBe("The command 'multi' specifies an argument vector that is not the last argument.");
});
}
[Fact]
public void Should_Assign_Values_To_Argument_Vector()
[Fact]
public void Should_Throw_If_An_Argument_Vector_Is_Not_Specified_Last()
{
// Given
var app = new CommandApp();
app.Configure(config =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<GenericCommand<ArgumentVectorSettings>>("multi");
});
config.PropagateExceptions();
config.AddCommand<GenericCommand<MultipleArgumentVectorSpecifiedFirstSettings>>("multi");
});
// When
var result = app.Run(new[]
{
"multi", "a", "b", "c",
});
// When
var result = Record.Exception(() => app.Run(new[] { "multi", "a", "b", "c" }));
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<ArgumentVectorSettings>().And(vec =>
{
vec.Foo.Length.ShouldBe(3);
vec.Foo[0].ShouldBe("a");
vec.Foo[1].ShouldBe("b");
vec.Foo[2].ShouldBe("c");
});
}
[Fact]
public void Should_Assign_Values_To_Option_Vector()
// Then
result.ShouldBeOfType<CommandConfigurationException>().And(ex =>
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<OptionVectorCommand>("cmd");
});
ex.Message.ShouldBe("The command 'multi' specifies an argument vector that is not the last argument.");
});
}
// When
var result = app.Run(new[]
{
"cmd", "--foo", "red",
"--bar", "4", "--foo", "blue",
});
[Fact]
public void Should_Assign_Values_To_Argument_Vector()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<GenericCommand<ArgumentVectorSettings>>("multi");
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<OptionVectorSettings>().And(vec =>
{
vec.Foo.ShouldBe(new string[] { "red", "blue" });
vec.Bar.ShouldBe(new int[] { 4 });
});
}
// When
var result = app.Run(new[]
{
"multi", "a", "b", "c",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<ArgumentVectorSettings>().And(vec =>
{
vec.Foo.Length.ShouldBe(3);
vec.Foo[0].ShouldBe("a");
vec.Foo[1].ShouldBe("b");
vec.Foo[2].ShouldBe("c");
});
}
[Fact]
public void Should_Assign_Values_To_Option_Vector()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.PropagateExceptions();
config.AddCommand<OptionVectorCommand>("cmd");
});
// When
var result = app.Run(new[]
{
"cmd", "--foo", "red",
"--bar", "4", "--foo", "blue",
});
// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<OptionVectorSettings>().And(vec =>
{
vec.Foo.ShouldBe(new string[] { "red", "blue" });
vec.Bar.ShouldBe(new int[] { 4 });
});
}
}
}
}

View File

@ -1,37 +1,31 @@
using Shouldly;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
public sealed class Version
{
public sealed class Version
[Fact]
public void Should_Output_The_Version_To_The_Console()
{
[Fact]
public void Should_Output_The_Version_To_The_Console()
// Given
var fixture = new CommandAppTester();
fixture.Configure(config =>
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(config =>
config.AddBranch<AnimalSettings>("animal", animal =>
{
config.AddBranch<AnimalSettings>("animal", animal =>
animal.AddBranch<MammalSettings>("mammal", mammal =>
{
animal.AddBranch<MammalSettings>("mammal", mammal =>
{
mammal.AddCommand<DogCommand>("dog");
mammal.AddCommand<HorseCommand>("horse");
});
mammal.AddCommand<DogCommand>("dog");
mammal.AddCommand<HorseCommand>("horse");
});
});
});
// When
var result = fixture.Run(Constants.VersionCommand);
// When
var result = fixture.Run(Constants.VersionCommand);
// Then
result.Output.ShouldStartWith("Spectre.Cli version ");
}
// Then
result.Output.ShouldStartWith("Spectre.Cli version ");
}
}
}
}

View File

@ -1,157 +1,148 @@
using System.Threading.Tasks;
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Spectre.Verify.Extensions;
using VerifyXunit;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed partial class CommandAppTests
{
public sealed partial class CommandAppTests
[UsesVerify]
[ExpectationPath("Cli/Xml")]
public sealed class Xml
{
[UsesVerify]
[ExpectationPath("Cli/Xml")]
public sealed class Xml
[Fact]
[Expectation("Test_1")]
public Task Should_Dump_Correct_Model_For_Case_1()
{
[Fact]
[Expectation("Test_1")]
public Task Should_Dump_Correct_Model_For_Case_1()
// Given
var fixture = new CommandAppTester();
fixture.Configure(config =>
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(config =>
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
{
config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal =>
animal.AddBranch<MammalSettings>("mammal", mammal =>
{
animal.AddBranch<MammalSettings>("mammal", mammal =>
{
mammal.AddCommand<DogCommand>("dog");
mammal.AddCommand<HorseCommand>("horse");
});
mammal.AddCommand<DogCommand>("dog");
mammal.AddCommand<HorseCommand>("horse");
});
});
});
// When
var result = fixture.Run(Constants.XmlDocCommand);
// When
var result = fixture.Run(Constants.XmlDocCommand);
// Then
return Verifier.Verify(result.Output);
}
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Test_2")]
public Task Should_Dump_Correct_Model_For_Case_2()
[Fact]
[Expectation("Test_2")]
public Task Should_Dump_Correct_Model_For_Case_2()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(config =>
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(config =>
config.AddCommand<DogCommand>("dog");
});
// When
var result = fixture.Run(Constants.XmlDocCommand);
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Test_3")]
public Task Should_Dump_Correct_Model_For_Case_3()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(config =>
{
config.AddBranch<AnimalSettings>("animal", animal =>
{
config.AddCommand<DogCommand>("dog");
animal.AddCommand<DogCommand>("dog");
animal.AddCommand<HorseCommand>("horse");
});
});
// When
var result = fixture.Run(Constants.XmlDocCommand);
// When
var result = fixture.Run(Constants.XmlDocCommand);
// Then
return Verifier.Verify(result.Output);
}
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Test_3")]
public Task Should_Dump_Correct_Model_For_Case_3()
[Fact]
[Expectation("Test_4")]
public Task Should_Dump_Correct_Model_For_Case_4()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(config =>
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(config =>
config.AddBranch<AnimalSettings>("animal", animal =>
{
config.AddBranch<AnimalSettings>("animal", animal =>
{
animal.AddCommand<DogCommand>("dog");
animal.AddCommand<HorseCommand>("horse");
});
animal.AddCommand<DogCommand>("dog");
});
});
// When
var result = fixture.Run(Constants.XmlDocCommand);
// When
var result = fixture.Run(Constants.XmlDocCommand);
// Then
return Verifier.Verify(result.Output);
}
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Test_4")]
public Task Should_Dump_Correct_Model_For_Case_4()
[Fact]
[Expectation("Test_5")]
public Task Should_Dump_Correct_Model_For_Case_5()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(config =>
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(config =>
{
config.AddBranch<AnimalSettings>("animal", animal =>
{
animal.AddCommand<DogCommand>("dog");
});
});
config.AddCommand<OptionVectorCommand>("cmd");
});
// When
var result = fixture.Run(Constants.XmlDocCommand);
// When
var result = fixture.Run(Constants.XmlDocCommand);
// Then
return Verifier.Verify(result.Output);
}
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Test_5")]
public Task Should_Dump_Correct_Model_For_Case_5()
[Fact]
[Expectation("Test_6")]
public Task Should_Dump_Correct_Model_For_Model_With_Default_Command()
{
// Given
var fixture = new CommandAppTester();
fixture.SetDefaultCommand<DogCommand>();
fixture.Configure(config =>
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(config =>
{
config.AddCommand<OptionVectorCommand>("cmd");
});
config.AddCommand<HorseCommand>("horse");
});
// When
var result = fixture.Run(Constants.XmlDocCommand);
// When
var result = fixture.Run(Constants.XmlDocCommand);
// Then
return Verifier.Verify(result.Output);
}
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Test_6")]
public Task Should_Dump_Correct_Model_For_Model_With_Default_Command()
{
// Given
var fixture = new CommandAppTester();
fixture.SetDefaultCommand<DogCommand>();
fixture.Configure(config =>
{
config.AddCommand<HorseCommand>("horse");
});
[Fact]
[Expectation("Hidden_Command_Options")]
public Task Should_Not_Dump_Hidden_Options_On_A_Command()
{
// Given
var fixture = new CommandAppTester();
fixture.SetDefaultCommand<HiddenOptionsCommand>();
// When
var result = fixture.Run(Constants.XmlDocCommand);
// When
var result = fixture.Run(Constants.XmlDocCommand);
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Hidden_Command_Options")]
public Task Should_Not_Dump_Hidden_Options_On_A_Command()
{
// Given
var fixture = new CommandAppTester();
fixture.SetDefaultCommand<HiddenOptionsCommand>();
// When
var result = fixture.Run(Constants.XmlDocCommand);
// Then
return Verifier.Verify(result.Output);
}
// Then
return Verifier.Verify(result.Output);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,11 @@
using Spectre.Console.Cli;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit.Cli;
namespace Spectre.Console.Tests.Unit.Cli
public sealed class DefaultTypeRegistrarTests
{
public sealed class DefaultTypeRegistrarTests
[Fact]
public void Should_Pass_Base_Registrar_Tests()
{
[Fact]
public void Should_Pass_Base_Registrar_Tests()
{
var harness = new TypeRegistrarBaseTests(() => new DefaultTypeRegistrar());
harness.RunAllTests();
}
var harness = new TypeRegistrarBaseTests(() => new DefaultTypeRegistrar());
harness.RunAllTests();
}
}
}

View File

@ -1,23 +1,19 @@
using Shouldly;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public sealed class ColorSystemTests
{
public sealed class ColorSystemTests
[Theory]
[InlineData(ColorSystem.NoColors, ColorSystemSupport.NoColors)]
[InlineData(ColorSystem.Legacy, ColorSystemSupport.Legacy)]
[InlineData(ColorSystem.Standard, ColorSystemSupport.Standard)]
[InlineData(ColorSystem.EightBit, ColorSystemSupport.EightBit)]
[InlineData(ColorSystem.TrueColor, ColorSystemSupport.TrueColor)]
public void Should_Be_Analog_To_ColorSystemSupport(ColorSystem colors, ColorSystemSupport support)
{
[Theory]
[InlineData(ColorSystem.NoColors, ColorSystemSupport.NoColors)]
[InlineData(ColorSystem.Legacy, ColorSystemSupport.Legacy)]
[InlineData(ColorSystem.Standard, ColorSystemSupport.Standard)]
[InlineData(ColorSystem.EightBit, ColorSystemSupport.EightBit)]
[InlineData(ColorSystem.TrueColor, ColorSystemSupport.TrueColor)]
public void Should_Be_Analog_To_ColorSystemSupport(ColorSystem colors, ColorSystemSupport support)
{
// Given, When
var result = (int)colors;
// Given, When
var result = (int)colors;
// Then
result.ShouldBe((int)support);
}
// Then
result.ShouldBe((int)support);
}
}
}

View File

@ -1,287 +1,280 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Shouldly;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public sealed class ColorTests
{
public sealed class ColorTests
public sealed class TheEqualsMethod
{
public sealed class TheEqualsMethod
[Fact]
public void Should_Consider_Color_And_Non_Color_Equal()
{
[Fact]
public void Should_Consider_Color_And_Non_Color_Equal()
{
// Given
var color1 = new Color(128, 0, 128);
// Given
var color1 = new Color(128, 0, 128);
// When
var result = color1.Equals("Foo");
// When
var result = color1.Equals("Foo");
// Then
result.ShouldBeFalse();
}
[Fact]
public void Should_Consider_Same_Colors_Equal_By_Component()
{
// Given
var color1 = new Color(128, 0, 128);
var color2 = new Color(128, 0, 128);
// When
var result = color1.Equals(color2);
// Then
result.ShouldBeTrue();
}
[Fact]
public void Should_Consider_Same_Known_Colors_Equal()
{
// Given
var color1 = Color.Cyan1;
var color2 = Color.Cyan1;
// When
var result = color1.Equals(color2);
// Then
result.ShouldBeTrue();
}
[Fact]
public void Should_Consider_Known_Color_And_Color_With_Same_Components_Equal()
{
// Given
var color1 = Color.Cyan1;
var color2 = new Color(0, 255, 255);
// When
var result = color1.Equals(color2);
// Then
result.ShouldBeTrue();
}
[Fact]
public void Should_Not_Consider_Different_Colors_Equal()
{
// Given
var color1 = new Color(128, 0, 128);
var color2 = new Color(128, 128, 128);
// When
var result = color1.Equals(color2);
// Then
result.ShouldBeFalse();
}
[Fact]
public void Shourd_Not_Consider_Black_And_Default_Colors_Equal()
{
// Given
var color1 = Color.Default;
var color2 = Color.Black;
// When
var result = color1.Equals(color2);
// Then
result.ShouldBeFalse();
}
}
public sealed class TheGetHashCodeMethod
{
[Fact]
public void Should_Return_Same_HashCode_For_Same_Colors()
{
// Given
var color1 = new Color(128, 0, 128);
var color2 = new Color(128, 0, 128);
// When
var hash1 = color1.GetHashCode();
var hash2 = color2.GetHashCode();
// Then
hash1.ShouldBe(hash2);
}
[Fact]
public void Should_Return_Different_HashCode_For_Different_Colors()
{
// Given
var color1 = new Color(128, 0, 128);
var color2 = new Color(128, 128, 128);
// When
var hash1 = color1.GetHashCode();
var hash2 = color2.GetHashCode();
// Then
hash1.ShouldNotBe(hash2);
}
}
public sealed class ImplicitConversions
{
public sealed class Int32ToColor
{
public static IEnumerable<object[]> Data =>
Enumerable.Range(0, 255)
.Select(number => new object[] { number });
[Theory]
[MemberData(nameof(Data))]
public void Should_Return_Expected_Color(int number)
{
// Given, When
var result = (Color)number;
// Then
result.ShouldBeFalse();
result.ShouldBe(Color.FromInt32(number));
}
[Fact]
public void Should_Consider_Same_Colors_Equal_By_Component()
public void Should_Throw_If_Integer_Is_Lower_Than_Zero()
{
// Given
var color1 = new Color(128, 0, 128);
var color2 = new Color(128, 0, 128);
// When
var result = color1.Equals(color2);
// Given, When
var result = Record.Exception(() => (Color)(-1));
// Then
result.ShouldBeTrue();
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("Color number must be between 0 and 255");
}
[Fact]
public void Should_Consider_Same_Known_Colors_Equal()
public void Should_Throw_If_Integer_Is_Higher_Than_255()
{
// Given
var color1 = Color.Cyan1;
var color2 = Color.Cyan1;
// When
var result = color1.Equals(color2);
// Given, When
var result = Record.Exception(() => (Color)256);
// Then
result.ShouldBeTrue();
}
[Fact]
public void Should_Consider_Known_Color_And_Color_With_Same_Components_Equal()
{
// Given
var color1 = Color.Cyan1;
var color2 = new Color(0, 255, 255);
// When
var result = color1.Equals(color2);
// Then
result.ShouldBeTrue();
}
[Fact]
public void Should_Not_Consider_Different_Colors_Equal()
{
// Given
var color1 = new Color(128, 0, 128);
var color2 = new Color(128, 128, 128);
// When
var result = color1.Equals(color2);
// Then
result.ShouldBeFalse();
}
[Fact]
public void Shourd_Not_Consider_Black_And_Default_Colors_Equal()
{
// Given
var color1 = Color.Default;
var color2 = Color.Black;
// When
var result = color1.Equals(color2);
// Then
result.ShouldBeFalse();
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("Color number must be between 0 and 255");
}
}
public sealed class TheGetHashCodeMethod
public sealed class ConsoleColorToColor
{
[Fact]
public void Should_Return_Same_HashCode_For_Same_Colors()
[Theory]
[InlineData(ConsoleColor.Black, 0)]
[InlineData(ConsoleColor.DarkRed, 1)]
[InlineData(ConsoleColor.DarkGreen, 2)]
[InlineData(ConsoleColor.DarkYellow, 3)]
[InlineData(ConsoleColor.DarkBlue, 4)]
[InlineData(ConsoleColor.DarkMagenta, 5)]
[InlineData(ConsoleColor.DarkCyan, 6)]
[InlineData(ConsoleColor.Gray, 7)]
[InlineData(ConsoleColor.DarkGray, 8)]
[InlineData(ConsoleColor.Red, 9)]
[InlineData(ConsoleColor.Green, 10)]
[InlineData(ConsoleColor.Yellow, 11)]
[InlineData(ConsoleColor.Blue, 12)]
[InlineData(ConsoleColor.Magenta, 13)]
[InlineData(ConsoleColor.Cyan, 14)]
[InlineData(ConsoleColor.White, 15)]
public void Should_Return_Expected_Color(ConsoleColor color, int expected)
{
// Given
var color1 = new Color(128, 0, 128);
var color2 = new Color(128, 0, 128);
// When
var hash1 = color1.GetHashCode();
var hash2 = color2.GetHashCode();
// Given, When
var result = (Color)color;
// Then
hash1.ShouldBe(hash2);
}
[Fact]
public void Should_Return_Different_HashCode_For_Different_Colors()
{
// Given
var color1 = new Color(128, 0, 128);
var color2 = new Color(128, 128, 128);
// When
var hash1 = color1.GetHashCode();
var hash2 = color2.GetHashCode();
// Then
hash1.ShouldNotBe(hash2);
result.ShouldBe(Color.FromInt32(expected));
}
}
public sealed class ImplicitConversions
public sealed class ColorToConsoleColor
{
public sealed class Int32ToColor
{
public static IEnumerable<object[]> Data =>
Enumerable.Range(0, 255)
.Select(number => new object[] { number });
[Theory]
[MemberData(nameof(Data))]
public void Should_Return_Expected_Color(int number)
{
// Given, When
var result = (Color)number;
// Then
result.ShouldBe(Color.FromInt32(number));
}
[Fact]
public void Should_Throw_If_Integer_Is_Lower_Than_Zero()
{
// Given, When
var result = Record.Exception(() => (Color)(-1));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("Color number must be between 0 and 255");
}
[Fact]
public void Should_Throw_If_Integer_Is_Higher_Than_255()
{
// Given, When
var result = Record.Exception(() => (Color)256);
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("Color number must be between 0 and 255");
}
}
public sealed class ConsoleColorToColor
{
[Theory]
[InlineData(ConsoleColor.Black, 0)]
[InlineData(ConsoleColor.DarkRed, 1)]
[InlineData(ConsoleColor.DarkGreen, 2)]
[InlineData(ConsoleColor.DarkYellow, 3)]
[InlineData(ConsoleColor.DarkBlue, 4)]
[InlineData(ConsoleColor.DarkMagenta, 5)]
[InlineData(ConsoleColor.DarkCyan, 6)]
[InlineData(ConsoleColor.Gray, 7)]
[InlineData(ConsoleColor.DarkGray, 8)]
[InlineData(ConsoleColor.Red, 9)]
[InlineData(ConsoleColor.Green, 10)]
[InlineData(ConsoleColor.Yellow, 11)]
[InlineData(ConsoleColor.Blue, 12)]
[InlineData(ConsoleColor.Magenta, 13)]
[InlineData(ConsoleColor.Cyan, 14)]
[InlineData(ConsoleColor.White, 15)]
public void Should_Return_Expected_Color(ConsoleColor color, int expected)
{
// Given, When
var result = (Color)color;
// Then
result.ShouldBe(Color.FromInt32(expected));
}
}
public sealed class ColorToConsoleColor
{
[Theory]
[InlineData(0, ConsoleColor.Black)]
[InlineData(1, ConsoleColor.DarkRed)]
[InlineData(2, ConsoleColor.DarkGreen)]
[InlineData(3, ConsoleColor.DarkYellow)]
[InlineData(4, ConsoleColor.DarkBlue)]
[InlineData(5, ConsoleColor.DarkMagenta)]
[InlineData(6, ConsoleColor.DarkCyan)]
[InlineData(7, ConsoleColor.Gray)]
[InlineData(8, ConsoleColor.DarkGray)]
[InlineData(9, ConsoleColor.Red)]
[InlineData(10, ConsoleColor.Green)]
[InlineData(11, ConsoleColor.Yellow)]
[InlineData(12, ConsoleColor.Blue)]
[InlineData(13, ConsoleColor.Magenta)]
[InlineData(14, ConsoleColor.Cyan)]
[InlineData(15, ConsoleColor.White)]
public void Should_Return_Expected_ConsoleColor_For_Known_Color(int color, ConsoleColor expected)
{
// Given, When
var result = (ConsoleColor)Color.FromInt32(color);
// Then
result.ShouldBe(expected);
}
}
}
public sealed class TheToMarkupMethod
{
[Fact]
public void Should_Return_Expected_Markup_For_Default_Color()
[Theory]
[InlineData(0, ConsoleColor.Black)]
[InlineData(1, ConsoleColor.DarkRed)]
[InlineData(2, ConsoleColor.DarkGreen)]
[InlineData(3, ConsoleColor.DarkYellow)]
[InlineData(4, ConsoleColor.DarkBlue)]
[InlineData(5, ConsoleColor.DarkMagenta)]
[InlineData(6, ConsoleColor.DarkCyan)]
[InlineData(7, ConsoleColor.Gray)]
[InlineData(8, ConsoleColor.DarkGray)]
[InlineData(9, ConsoleColor.Red)]
[InlineData(10, ConsoleColor.Green)]
[InlineData(11, ConsoleColor.Yellow)]
[InlineData(12, ConsoleColor.Blue)]
[InlineData(13, ConsoleColor.Magenta)]
[InlineData(14, ConsoleColor.Cyan)]
[InlineData(15, ConsoleColor.White)]
public void Should_Return_Expected_ConsoleColor_For_Known_Color(int color, ConsoleColor expected)
{
// Given, When
var result = Color.Default.ToMarkup();
var result = (ConsoleColor)Color.FromInt32(color);
// Then
result.ShouldBe("default");
}
[Fact]
public void Should_Return_Expected_Markup_For_Known_Color()
{
// Given, When
var result = Color.Red.ToMarkup();
// Then
result.ShouldBe("red");
}
[Fact]
public void Should_Return_Expected_Markup_For_Custom_Color()
{
// Given, When
var result = new Color(255, 1, 12).ToMarkup();
// Then
result.ShouldBe("#FF010C");
}
}
public sealed class TheToStringMethod
{
[Fact]
public void Should_Return_Color_Name_For_Known_Colors()
{
// Given, When
var name = Color.Fuchsia.ToString();
// Then
name.ShouldBe("fuchsia");
}
[Fact]
public void Should_Return_Hex_String_For_Unknown_Colors()
{
// Given, When
var name = new Color(128, 0, 128).ToString();
// Then
name.ShouldBe("#800080 (RGB=128,0,128)");
result.ShouldBe(expected);
}
}
}
}
public sealed class TheToMarkupMethod
{
[Fact]
public void Should_Return_Expected_Markup_For_Default_Color()
{
// Given, When
var result = Color.Default.ToMarkup();
// Then
result.ShouldBe("default");
}
[Fact]
public void Should_Return_Expected_Markup_For_Known_Color()
{
// Given, When
var result = Color.Red.ToMarkup();
// Then
result.ShouldBe("red");
}
[Fact]
public void Should_Return_Expected_Markup_For_Custom_Color()
{
// Given, When
var result = new Color(255, 1, 12).ToMarkup();
// Then
result.ShouldBe("#FF010C");
}
}
public sealed class TheToStringMethod
{
[Fact]
public void Should_Return_Color_Name_For_Known_Colors()
{
// Given, When
var name = Color.Fuchsia.ToString();
// Then
name.ShouldBe("fuchsia");
}
[Fact]
public void Should_Return_Hex_String_For_Unknown_Colors()
{
// Given, When
var name = new Color(128, 0, 128).ToString();
// Then
name.ShouldBe("#800080 (RGB=128,0,128)");
}
}
}

View File

@ -1,96 +1,91 @@
using Shouldly;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public sealed class EmojiTests
{
public sealed class EmojiTests
[Fact]
public void Should_Substitute_Emoji_Shortcodes_In_Markdown()
{
// Given
var console = new TestConsole();
// When
console.Markup("Hello :globe_showing_europe_africa:!");
// Then
console.Output.ShouldBe("Hello 🌍!");
}
[Fact]
public void Should_Contain_Predefined_Emojis()
{
// Given, When
const string result = "Hello " + Emoji.Known.GlobeShowingEuropeAfrica + "!";
// Then
result.ShouldBe("Hello 🌍!");
}
public sealed class TheReplaceMethod
{
[Fact]
public void Should_Substitute_Emoji_Shortcodes_In_Markdown()
public void Should_Replace_Emojis_In_Text()
{
// Given, When
var result = Emoji.Replace("Hello :globe_showing_europe_africa:!");
// Then
result.ShouldBe("Hello 🌍!");
}
}
public sealed class Parsing
{
[Theory]
[InlineData(":", ":")]
[InlineData("::", "::")]
[InlineData(":::", ":::")]
[InlineData("::::", "::::")]
[InlineData("::i:", "::i:")]
[InlineData(":i:i:", ":i:i:")]
[InlineData("::globe_showing_europe_africa::", ":🌍:")]
[InlineData(":globe_showing_europe_africa::globe_showing_europe_africa:", "🌍🌍")]
[InlineData("::globe_showing_europe_africa:::test:::globe_showing_europe_africa:::", ":🌍::test::🌍::")]
public void Can_Handle_Different_Combinations(string markup, string expected)
{
// Given
var console = new TestConsole();
// When
console.Markup("Hello :globe_showing_europe_africa:!");
console.Markup(markup);
// Then
console.Output.ShouldBe("Hello 🌍!");
console.Output.ShouldBe(expected);
}
[Fact]
public void Should_Contain_Predefined_Emojis()
public void Should_Leave_Single_Colons()
{
// Given, When
const string result = "Hello " + Emoji.Known.GlobeShowingEuropeAfrica + "!";
// Given
var console = new TestConsole();
// When
console.Markup("Hello :globe_showing_europe_africa:! Output: good");
// Then
result.ShouldBe("Hello 🌍!");
console.Output.ShouldBe("Hello 🌍! Output: good");
}
public sealed class TheReplaceMethod
[Fact]
public void Unknown_emojis_should_remain_unchanged()
{
[Fact]
public void Should_Replace_Emojis_In_Text()
{
// Given, When
var result = Emoji.Replace("Hello :globe_showing_europe_africa:!");
// Given
var console = new TestConsole();
// Then
result.ShouldBe("Hello 🌍!");
}
}
// When
console.Markup("Hello :globe_showing_flat_earth:!");
public sealed class Parsing
{
[Theory]
[InlineData(":", ":")]
[InlineData("::", "::")]
[InlineData(":::", ":::")]
[InlineData("::::", "::::")]
[InlineData("::i:", "::i:")]
[InlineData(":i:i:", ":i:i:")]
[InlineData("::globe_showing_europe_africa::", ":🌍:")]
[InlineData(":globe_showing_europe_africa::globe_showing_europe_africa:", "🌍🌍")]
[InlineData("::globe_showing_europe_africa:::test:::globe_showing_europe_africa:::", ":🌍::test::🌍::")]
public void Can_Handle_Different_Combinations(string markup, string expected)
{
// Given
var console = new TestConsole();
// When
console.Markup(markup);
// Then
console.Output.ShouldBe(expected);
}
[Fact]
public void Should_Leave_Single_Colons()
{
// Given
var console = new TestConsole();
// When
console.Markup("Hello :globe_showing_europe_africa:! Output: good");
// Then
console.Output.ShouldBe("Hello 🌍! Output: good");
}
[Fact]
public void Unknown_emojis_should_remain_unchanged()
{
// Given
var console = new TestConsole();
// When
console.Markup("Hello :globe_showing_flat_earth:!");
// Then
console.Output.ShouldBe("Hello :globe_showing_flat_earth:!");
}
// Then
console.Output.ShouldBe("Hello :globe_showing_flat_earth:!");
}
}
}
}

View File

@ -1,104 +1,95 @@
using System;
using System.Threading.Tasks;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;
using Spectre.Verify.Extensions;
using VerifyXunit;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
[UsesVerify]
[ExpectationPath("Exception")]
public sealed class ExceptionTests
{
[UsesVerify]
[ExpectationPath("Exception")]
public sealed class ExceptionTests
[Fact]
[Expectation("Default")]
public Task Should_Write_Exception()
{
[Fact]
[Expectation("Default")]
public Task Should_Write_Exception()
{
// Given
var console = new TestConsole().Width(1024);
var dex = GetException(() => TestExceptions.MethodThatThrows(null));
// Given
var console = new TestConsole().Width(1024);
var dex = GetException(() => TestExceptions.MethodThatThrows(null));
// When
var result = console.WriteNormalizedException(dex);
// When
var result = console.WriteNormalizedException(dex);
// Then
return Verifier.Verify(result);
}
[Fact]
[Expectation("ShortenedTypes")]
public Task Should_Write_Exception_With_Shortened_Types()
{
// Given
var console = new TestConsole().Width(1024);
var dex = GetException(() => TestExceptions.MethodThatThrows(null));
// When
var result = console.WriteNormalizedException(dex, ExceptionFormats.ShortenTypes);
// Then
return Verifier.Verify(result);
}
[Fact]
[Expectation("ShortenedMethods")]
public Task Should_Write_Exception_With_Shortened_Methods()
{
// Given
var console = new TestConsole().Width(1024);
var dex = GetException(() => TestExceptions.MethodThatThrows(null));
// When
var result = console.WriteNormalizedException(dex, ExceptionFormats.ShortenMethods);
// Then
return Verifier.Verify(result);
}
[Fact]
[Expectation("InnerException")]
public Task Should_Write_Exception_With_Inner_Exception()
{
// Given
var console = new TestConsole().Width(1024);
var dex = GetException(() => TestExceptions.ThrowWithInnerException());
// When
var result = console.WriteNormalizedException(dex);
// Then
return Verifier.Verify(result);
}
[Fact]
[Expectation("CallSite")]
public Task Should_Write_Exceptions_With_Generic_Type_Parameters_In_Callsite_As_Expected()
{
// Given
var console = new TestConsole().Width(1024);
var dex = GetException(() => TestExceptions.ThrowWithGenericInnerException());
// When
var result = console.WriteNormalizedException(dex);
// Then
return Verifier.Verify(result);
}
public static Exception GetException(Action action)
{
try
{
action?.Invoke();
}
catch (Exception e)
{
return e;
}
throw new InvalidOperationException("Exception harness failed");
}
// Then
return Verifier.Verify(result);
}
}
[Fact]
[Expectation("ShortenedTypes")]
public Task Should_Write_Exception_With_Shortened_Types()
{
// Given
var console = new TestConsole().Width(1024);
var dex = GetException(() => TestExceptions.MethodThatThrows(null));
// When
var result = console.WriteNormalizedException(dex, ExceptionFormats.ShortenTypes);
// Then
return Verifier.Verify(result);
}
[Fact]
[Expectation("ShortenedMethods")]
public Task Should_Write_Exception_With_Shortened_Methods()
{
// Given
var console = new TestConsole().Width(1024);
var dex = GetException(() => TestExceptions.MethodThatThrows(null));
// When
var result = console.WriteNormalizedException(dex, ExceptionFormats.ShortenMethods);
// Then
return Verifier.Verify(result);
}
[Fact]
[Expectation("InnerException")]
public Task Should_Write_Exception_With_Inner_Exception()
{
// Given
var console = new TestConsole().Width(1024);
var dex = GetException(() => TestExceptions.ThrowWithInnerException());
// When
var result = console.WriteNormalizedException(dex);
// Then
return Verifier.Verify(result);
}
[Fact]
[Expectation("CallSite")]
public Task Should_Write_Exceptions_With_Generic_Type_Parameters_In_Callsite_As_Expected()
{
// Given
var console = new TestConsole().Width(1024);
var dex = GetException(() => TestExceptions.ThrowWithGenericInnerException());
// When
var result = console.WriteNormalizedException(dex);
// Then
return Verifier.Verify(result);
}
public static Exception GetException(Action action)
{
try
{
action?.Invoke();
}
catch (Exception e)
{
return e;
}
throw new InvalidOperationException("Exception harness failed");
}
}

View File

@ -1,30 +1,25 @@
using System.Globalization;
using Shouldly;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public sealed class DownloadedColumnTests
{
public sealed class DownloadedColumnTests
[Theory]
[InlineData(0, 1, "0/1 byte")]
[InlineData(37, 101, "37/101 bytes")]
[InlineData(101, 101, "101 bytes")]
[InlineData(512, 1024, "0.5/1.0 KB")]
[InlineData(1024, 1024, "1.0 KB")]
[InlineData(1024 * 512, 5 * 1024 * 1024, "0.5/5.0 MB")]
[InlineData(5 * 1024 * 1024, 5 * 1024 * 1024, "5.0 MB")]
public void Should_Return_Correct_Value(double value, double total, string expected)
{
[Theory]
[InlineData(0, 1, "0/1 byte")]
[InlineData(37, 101, "37/101 bytes")]
[InlineData(101, 101, "101 bytes")]
[InlineData(512, 1024, "0.5/1.0 KB")]
[InlineData(1024, 1024, "1.0 KB")]
[InlineData(1024 * 512, 5 * 1024 * 1024, "0.5/5.0 MB")]
[InlineData(5 * 1024 * 1024, 5 * 1024 * 1024, "5.0 MB")]
public void Should_Return_Correct_Value(double value, double total, string expected)
{
// Given
var fixture = new ProgressColumnFixture<DownloadedColumn>(value, total);
fixture.Column.Culture = CultureInfo.InvariantCulture;
// Given
var fixture = new ProgressColumnFixture<DownloadedColumn>(value, total);
fixture.Column.Culture = CultureInfo.InvariantCulture;
// When
var result = fixture.Render();
// When
var result = fixture.Render();
// Then
result.ShouldBe(expected);
}
// Then
result.ShouldBe(expected);
}
}
}

View File

@ -1,28 +1,23 @@
using System;
using Spectre.Console.Rendering;
using Spectre.Console.Testing;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public sealed class ProgressColumnFixture<T>
where T : ProgressColumn, new()
{
public sealed class ProgressColumnFixture<T>
where T : ProgressColumn, new()
public T Column { get; }
public ProgressTask Task { get; set; }
public ProgressColumnFixture(double completed, double total)
{
public T Column { get; }
public ProgressTask Task { get; set; }
public ProgressColumnFixture(double completed, double total)
{
Column = new T();
Task = new ProgressTask(1, "Foo", total);
Task.Increment(completed);
}
public string Render()
{
var console = new TestConsole();
var context = new RenderContext(console.Profile.Capabilities);
console.Write(Column.Render(context, Task, TimeSpan.Zero));
return console.Output;
}
Column = new T();
Task = new ProgressTask(1, "Foo", total);
Task.Increment(completed);
}
}
public string Render()
{
var console = new TestConsole();
var context = new RenderContext(console.Profile.Capabilities);
console.Write(Column.Render(context, Task, TimeSpan.Zero));
return console.Output;
}
}

View File

@ -1,275 +1,267 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Shouldly;
using Spectre.Console.Testing;
using Spectre.Verify.Extensions;
using VerifyXunit;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
[UsesVerify]
[ExpectationPath("Live/Progress")]
public sealed class ProgressTests
{
[UsesVerify]
[ExpectationPath("Live/Progress")]
public sealed class ProgressTests
[Fact]
public void Should_Render_Task_Correctly()
{
[Fact]
public void Should_Render_Task_Correctly()
{
// Given
var console = new TestConsole()
.Width(10)
.Interactive()
.EmitAnsiSequences();
// Given
var console = new TestConsole()
.Width(10)
.Interactive()
.EmitAnsiSequences();
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(true);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(true);
// When
progress.Start(ctx => ctx.AddTask("foo"));
// When
progress.Start(ctx => ctx.AddTask("foo"));
// Then
console.Output
.NormalizeLineEndings()
.ShouldBe(
"[?25l" + // Hide cursor
" \n" + // Top padding
"━━━━━━━━━━\n" + // Task
" " + // Bottom padding
"[?25h"); // Clear + show cursor
}
// Then
console.Output
.NormalizeLineEndings()
.ShouldBe(
"[?25l" + // Hide cursor
" \n" + // Top padding
"━━━━━━━━━━\n" + // Task
" " + // Bottom padding
"[?25h"); // Clear + show cursor
}
[Fact]
public void Should_Not_Auto_Clear_If_Specified()
{
// Given
var console = new TestConsole()
.Width(10)
.Interactive()
.EmitAnsiSequences();
[Fact]
public void Should_Not_Auto_Clear_If_Specified()
{
// Given
var console = new TestConsole()
.Width(10)
.Interactive()
.EmitAnsiSequences();
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx => ctx.AddTask("foo"));
// When
progress.Start(ctx => ctx.AddTask("foo"));
// Then
console.Output
.NormalizeLineEndings()
.ShouldBe(
"[?25l" + // Hide cursor
" \n" + // Top padding
"━━━━━━━━━━\n" + // Task
" \n" + // Bottom padding
"[?25h"); // show cursor
}
// Then
console.Output
.NormalizeLineEndings()
.ShouldBe(
"[?25l" + // Hide cursor
" \n" + // Top padding
"━━━━━━━━━━\n" + // Task
" \n" + // Bottom padding
"[?25h"); // show cursor
}
[Fact]
[Expectation("Render_ReduceWidth")]
public Task Should_Reduce_Width_If_Needed()
{
// Given
var console = new TestConsole()
.Width(20)
.Interactive();
[Fact]
[Expectation("Render_ReduceWidth")]
public Task Should_Reduce_Width_If_Needed()
{
// Given
var console = new TestConsole()
.Width(20)
.Interactive();
var progress = new Progress(console)
.Columns(new ProgressColumn[]
{
var progress = new Progress(console)
.Columns(new ProgressColumn[]
{
new TaskDescriptionColumn(),
new ProgressBarColumn(),
new PercentageColumn(),
new RemainingTimeColumn(),
new SpinnerColumn(),
})
.AutoRefresh(false)
.AutoClear(false);
})
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
ctx.AddTask("foo");
ctx.AddTask("bar");
ctx.AddTask("baz");
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
public void Setting_Max_Value_Should_Set_The_MaxValue_And_Cap_Value()
// When
progress.Start(ctx =>
{
// Given
var console = new TestConsole()
.Interactive();
ctx.AddTask("foo");
ctx.AddTask("bar");
ctx.AddTask("baz");
});
var task = default(ProgressTask);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Increment(100);
task.MaxValue = 20;
});
// Then
task.MaxValue.ShouldBe(20);
task.Value.ShouldBe(20);
}
[Fact]
public void Setting_Value_Should_Override_Incremented_Value()
{
// Given
var console = new TestConsole()
.Interactive();
var task = default(ProgressTask);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Increment(50);
task.Value = 20;
});
// Then
task.MaxValue.ShouldBe(100);
task.Value.ShouldBe(20);
}
[Fact]
public void Setting_Value_To_MaxValue_Should_Finish_Task()
{
// Given
var console = new TestConsole()
.Interactive();
var task = default(ProgressTask);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Value = task.MaxValue;
});
// Then
task.IsFinished.ShouldBe(true);
}
[Fact]
public void Should_Increment_Manually_Set_Value()
{
// Given
var console = new TestConsole()
.Interactive();
var task = default(ProgressTask);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Value = 50;
task.Increment(10);
});
// Then
task.Value.ShouldBe(60);
}
[Fact]
public void Should_Hide_Completed_Tasks()
{
// Given
var console = new TestConsole()
.Width(10)
.Interactive()
.EmitAnsiSequences();
var taskFinished = default(ProgressTask);
var taskInProgress1 = default(ProgressTask);
var taskInProgress2 = default(ProgressTask);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false)
.HideCompleted(true);
// When
progress.Start(ctx =>
{
taskInProgress1 = ctx.AddTask("foo");
taskFinished = ctx.AddTask("bar");
taskInProgress2 = ctx.AddTask("baz");
taskInProgress2.Increment(20);
taskFinished.Value = taskFinished.MaxValue;
});
// Then
console.Output
.NormalizeLineEndings()
.ShouldBe(
"[?25l" + // Hide cursor
" \n" + // top padding
"━━━━━━━━━━\n" + // taskInProgress1
"━━━━━━━━━━\n" + // taskInProgress2
" \n" + // bottom padding
"[?25h"); // show cursor
}
[Fact]
public void Should_Report_Max_Remaining_Time_For_Extremely_Small_Progress()
{
// Given
var console = new TestConsole()
.Interactive();
var task = default(ProgressTask);
var progress = new Progress(console)
.Columns(new[] { new RemainingTimeColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Increment(double.Epsilon);
// Make sure that at least one millisecond has elapsed between the increments else the RemainingTime is null
// when the last timestamp is equal to the first timestamp of the samples.
Thread.Sleep(1);
task.Increment(double.Epsilon);
});
// Then
task.RemainingTime.ShouldBe(TimeSpan.MaxValue);
}
// Then
return Verifier.Verify(console.Output);
}
}
[Fact]
public void Setting_Max_Value_Should_Set_The_MaxValue_And_Cap_Value()
{
// Given
var console = new TestConsole()
.Interactive();
var task = default(ProgressTask);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Increment(100);
task.MaxValue = 20;
});
// Then
task.MaxValue.ShouldBe(20);
task.Value.ShouldBe(20);
}
[Fact]
public void Setting_Value_Should_Override_Incremented_Value()
{
// Given
var console = new TestConsole()
.Interactive();
var task = default(ProgressTask);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Increment(50);
task.Value = 20;
});
// Then
task.MaxValue.ShouldBe(100);
task.Value.ShouldBe(20);
}
[Fact]
public void Setting_Value_To_MaxValue_Should_Finish_Task()
{
// Given
var console = new TestConsole()
.Interactive();
var task = default(ProgressTask);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Value = task.MaxValue;
});
// Then
task.IsFinished.ShouldBe(true);
}
[Fact]
public void Should_Increment_Manually_Set_Value()
{
// Given
var console = new TestConsole()
.Interactive();
var task = default(ProgressTask);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Value = 50;
task.Increment(10);
});
// Then
task.Value.ShouldBe(60);
}
[Fact]
public void Should_Hide_Completed_Tasks()
{
// Given
var console = new TestConsole()
.Width(10)
.Interactive()
.EmitAnsiSequences();
var taskFinished = default(ProgressTask);
var taskInProgress1 = default(ProgressTask);
var taskInProgress2 = default(ProgressTask);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false)
.HideCompleted(true);
// When
progress.Start(ctx =>
{
taskInProgress1 = ctx.AddTask("foo");
taskFinished = ctx.AddTask("bar");
taskInProgress2 = ctx.AddTask("baz");
taskInProgress2.Increment(20);
taskFinished.Value = taskFinished.MaxValue;
});
// Then
console.Output
.NormalizeLineEndings()
.ShouldBe(
"[?25l" + // Hide cursor
" \n" + // top padding
"━━━━━━━━━━\n" + // taskInProgress1
"━━━━━━━━━━\n" + // taskInProgress2
" \n" + // bottom padding
"[?25h"); // show cursor
}
[Fact]
public void Should_Report_Max_Remaining_Time_For_Extremely_Small_Progress()
{
// Given
var console = new TestConsole()
.Interactive();
var task = default(ProgressTask);
var progress = new Progress(console)
.Columns(new[] { new RemainingTimeColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Increment(double.Epsilon);
// Make sure that at least one millisecond has elapsed between the increments else the RemainingTime is null
// when the last timestamp is equal to the first timestamp of the samples.
Thread.Sleep(1);
task.Increment(double.Epsilon);
});
// Then
task.RemainingTime.ShouldBe(TimeSpan.MaxValue);
}
}

View File

@ -1,61 +1,52 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Spectre.Console.Testing;
using Spectre.Verify.Extensions;
using VerifyXunit;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
[UsesVerify]
[ExpectationPath("Live/Status")]
public sealed class StatusTests
{
[UsesVerify]
[ExpectationPath("Live/Status")]
public sealed class StatusTests
public sealed class DummySpinner1 : Spinner
{
public sealed class DummySpinner1 : Spinner
{
public override TimeSpan Interval => TimeSpan.FromMilliseconds(100);
public override bool IsUnicode => true;
public override IReadOnlyList<string> Frames => new List<string> { "*", };
}
public sealed class DummySpinner2 : Spinner
{
public override TimeSpan Interval => TimeSpan.FromMilliseconds(100);
public override bool IsUnicode => true;
public override IReadOnlyList<string> Frames => new List<string> { "-", };
}
[Fact]
[Expectation("Render")]
public Task Should_Render_Status_Correctly()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.TrueColor)
.Width(10)
.Interactive()
.EmitAnsiSequences();
var status = new Status(console)
{
AutoRefresh = false,
Spinner = new DummySpinner1(),
};
// When
status.Start("foo", ctx =>
{
ctx.Refresh();
ctx.Spinner(new DummySpinner2());
ctx.Status("bar");
ctx.Refresh();
ctx.Spinner(new DummySpinner1());
ctx.Status("baz");
});
// Then
return Verifier.Verify(console.Output);
}
public override TimeSpan Interval => TimeSpan.FromMilliseconds(100);
public override bool IsUnicode => true;
public override IReadOnlyList<string> Frames => new List<string> { "*", };
}
}
public sealed class DummySpinner2 : Spinner
{
public override TimeSpan Interval => TimeSpan.FromMilliseconds(100);
public override bool IsUnicode => true;
public override IReadOnlyList<string> Frames => new List<string> { "-", };
}
[Fact]
[Expectation("Render")]
public Task Should_Render_Status_Correctly()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.TrueColor)
.Width(10)
.Interactive()
.EmitAnsiSequences();
var status = new Status(console)
{
AutoRefresh = false,
Spinner = new DummySpinner1(),
};
// When
status.Start("foo", ctx =>
{
ctx.Refresh();
ctx.Spinner(new DummySpinner2());
ctx.Status("bar");
ctx.Refresh();
ctx.Spinner(new DummySpinner1());
ctx.Status("baz");
});
// Then
return Verifier.Verify(console.Output);
}
}

View File

@ -1,156 +1,149 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Shouldly;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public sealed class MultiSelectionPromptTests
{
public sealed class MultiSelectionPromptTests
private class CustomItem
{
private class CustomItem
public int X { get; set; }
public int Y { get; set; }
public class Comparer : IEqualityComparer<CustomItem>
{
public int X { get; set; }
public int Y { get; set; }
public class Comparer : IEqualityComparer<CustomItem>
public bool Equals(CustomItem x, CustomItem y)
{
public bool Equals(CustomItem x, CustomItem y)
{
return x.X == y.X && x.Y == y.Y;
}
return x.X == y.X && x.Y == y.Y;
}
public int GetHashCode([DisallowNull] CustomItem obj)
{
throw new NotImplementedException();
}
public int GetHashCode([DisallowNull] CustomItem obj)
{
throw new NotSupportedException();
}
}
[Fact]
public void Should_Not_Mark_Item_As_Selected_By_Default()
{
// Given
var prompt = new MultiSelectionPrompt<int>();
// When
var choice = prompt.AddChoice(32);
// Then
choice.IsSelected.ShouldBeFalse();
}
[Fact]
public void Should_Mark_Item_As_Selected()
{
// Given
var prompt = new MultiSelectionPrompt<int>();
var choice = prompt.AddChoice(32);
// When
prompt.Select(32);
// Then
choice.IsSelected.ShouldBeTrue();
}
[Fact]
public void Should_Mark_Custom_Item_As_Selected_If_The_Same_Reference_Is_Used()
{
// Given
var prompt = new MultiSelectionPrompt<CustomItem>();
var item = new CustomItem { X = 18, Y = 32 };
var choice = prompt.AddChoice(item);
// When
prompt.Select(item);
// Then
choice.IsSelected.ShouldBeTrue();
}
[Fact]
public void Should_Mark_Custom_Item_As_Selected_If_A_Comparer_Is_Provided()
{
// Given
var prompt = new MultiSelectionPrompt<CustomItem>(new CustomItem.Comparer());
var choice = prompt.AddChoice(new CustomItem { X = 18, Y = 32 });
// When
prompt.Select(new CustomItem { X = 18, Y = 32 });
// Then
choice.IsSelected.ShouldBeTrue();
}
[Fact]
public void Should_Get_The_Direct_Parent()
{
// Given
var prompt = new MultiSelectionPrompt<string>();
prompt.AddChoice("root").AddChild("level-1").AddChild("level-2").AddChild("item");
// When
var actual = prompt.GetParent("item");
// Then
actual.ShouldBe("level-2");
}
[Fact]
public void Should_Get_The_List_Of_All_Parents()
{
// Given
var prompt = new MultiSelectionPrompt<string>();
prompt.AddChoice("root").AddChild("level-1").AddChild("level-2").AddChild("item");
// When
var actual = prompt.GetParents("item");
// Then
actual.ShouldBe(new[] { "root", "level-1", "level-2" });
}
[Fact]
public void Should_Get_An_Empty_List_Of_Parents_For_Root_Node()
{
// Given
var prompt = new MultiSelectionPrompt<string>();
prompt.AddChoice("root");
// When
var actual = prompt.GetParents("root");
// Then
actual.ShouldBeEmpty();
}
[Fact]
public void Should_Get_Null_As_Direct_Parent_Of_Root_Node()
{
// Given
var prompt = new MultiSelectionPrompt<string>();
prompt.AddChoice("root");
// When
var actual = prompt.GetParent("root");
// Then
actual.ShouldBeNull();
}
[Fact]
public void Should_Throw_When_Getting_Parents_Of_Non_Existing_Node()
{
// Given
var prompt = new MultiSelectionPrompt<string>();
prompt.AddChoice("root").AddChild("level-1").AddChild("level-2").AddChild("item");
// When
Action action = () => prompt.GetParents("non-existing");
// Then
action.ShouldThrow<ArgumentOutOfRangeException>();
}
}
}
[Fact]
public void Should_Not_Mark_Item_As_Selected_By_Default()
{
// Given
var prompt = new MultiSelectionPrompt<int>();
// When
var choice = prompt.AddChoice(32);
// Then
choice.IsSelected.ShouldBeFalse();
}
[Fact]
public void Should_Mark_Item_As_Selected()
{
// Given
var prompt = new MultiSelectionPrompt<int>();
var choice = prompt.AddChoice(32);
// When
prompt.Select(32);
// Then
choice.IsSelected.ShouldBeTrue();
}
[Fact]
public void Should_Mark_Custom_Item_As_Selected_If_The_Same_Reference_Is_Used()
{
// Given
var prompt = new MultiSelectionPrompt<CustomItem>();
var item = new CustomItem { X = 18, Y = 32 };
var choice = prompt.AddChoice(item);
// When
prompt.Select(item);
// Then
choice.IsSelected.ShouldBeTrue();
}
[Fact]
public void Should_Mark_Custom_Item_As_Selected_If_A_Comparer_Is_Provided()
{
// Given
var prompt = new MultiSelectionPrompt<CustomItem>(new CustomItem.Comparer());
var choice = prompt.AddChoice(new CustomItem { X = 18, Y = 32 });
// When
prompt.Select(new CustomItem { X = 18, Y = 32 });
// Then
choice.IsSelected.ShouldBeTrue();
}
[Fact]
public void Should_Get_The_Direct_Parent()
{
// Given
var prompt = new MultiSelectionPrompt<string>();
prompt.AddChoice("root").AddChild("level-1").AddChild("level-2").AddChild("item");
// When
var actual = prompt.GetParent("item");
// Then
actual.ShouldBe("level-2");
}
[Fact]
public void Should_Get_The_List_Of_All_Parents()
{
// Given
var prompt = new MultiSelectionPrompt<string>();
prompt.AddChoice("root").AddChild("level-1").AddChild("level-2").AddChild("item");
// When
var actual = prompt.GetParents("item");
// Then
actual.ShouldBe(new[] { "root", "level-1", "level-2" });
}
[Fact]
public void Should_Get_An_Empty_List_Of_Parents_For_Root_Node()
{
// Given
var prompt = new MultiSelectionPrompt<string>();
prompt.AddChoice("root");
// When
var actual = prompt.GetParents("root");
// Then
actual.ShouldBeEmpty();
}
[Fact]
public void Should_Get_Null_As_Direct_Parent_Of_Root_Node()
{
// Given
var prompt = new MultiSelectionPrompt<string>();
prompt.AddChoice("root");
// When
var actual = prompt.GetParent("root");
// Then
actual.ShouldBeNull();
}
[Fact]
public void Should_Throw_When_Getting_Parents_Of_Non_Existing_Node()
{
// Given
var prompt = new MultiSelectionPrompt<string>();
prompt.AddChoice("root").AddChild("level-1").AddChild("level-2").AddChild("item");
// When
Action action = () => prompt.GetParents("non-existing");
// Then
action.ShouldThrow<ArgumentOutOfRangeException>();
}
}

View File

@ -1,30 +1,24 @@
using System;
using Shouldly;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public sealed class SelectionPromptTests
{
public sealed class SelectionPromptTests
[Fact]
[GitHubIssue(608)]
public void Should_Not_Throw_When_Selecting_An_Item_With_Escaped_Markup()
{
[Fact]
[GitHubIssue(608)]
public void Should_Not_Throw_When_Selecting_An_Item_With_Escaped_Markup()
{
// Given
var console = new TestConsole();
console.Profile.Capabilities.Interactive = true;
console.Input.PushKey(ConsoleKey.Enter);
var input = "[red]This text will never be red[/]".EscapeMarkup();
// Given
var console = new TestConsole();
console.Profile.Capabilities.Interactive = true;
console.Input.PushKey(ConsoleKey.Enter);
var input = "[red]This text will never be red[/]".EscapeMarkup();
// When
var prompt = new SelectionPrompt<string>()
.Title("Select one")
.AddChoices(input);
prompt.Show(console);
// When
var prompt = new SelectionPrompt<string>()
.Title("Select one")
.AddChoices(input);
prompt.Show(console);
// Then
console.Output.ShouldContain(@"[red]This text will never be red[/]");
}
// Then
console.Output.ShouldContain(@"[red]This text will never be red[/]");
}
}
}

View File

@ -1,253 +1,244 @@
using System;
using System.Threading.Tasks;
using Shouldly;
using Spectre.Console.Testing;
using Spectre.Verify.Extensions;
using VerifyXunit;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
[UsesVerify]
[ExpectationPath("Prompts/Text")]
public sealed class TextPromptTests
{
[UsesVerify]
[ExpectationPath("Prompts/Text")]
public sealed class TextPromptTests
[Fact]
public void Should_Return_Entered_Text()
{
[Fact]
public void Should_Return_Entered_Text()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("Hello World");
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("Hello World");
// When
var result = console.Prompt(new TextPrompt<string>("Enter text:"));
// When
var result = console.Prompt(new TextPrompt<string>("Enter text:"));
// Then
result.ShouldBe("Hello World");
}
[Fact]
[Expectation("ConversionError")]
public Task Should_Return_Validation_Error_If_Value_Cannot_Be_Converted()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("ninety-nine");
console.Input.PushTextWithEnter("99");
// When
console.Prompt(new TextPrompt<int>("Age?"));
// Then
return Verifier.Verify(console.Lines);
}
[Fact]
[Expectation("DefaultValue")]
public Task Should_Chose_Default_Value_If_Nothing_Is_Entered()
{
// Given
var console = new TestConsole();
console.Input.PushKey(ConsoleKey.Enter);
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Banana")
.AddChoice("Orange")
.DefaultValue("Banana"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("InvalidChoice")]
public Task Should_Return_Error_If_An_Invalid_Choice_Is_Made()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("Apple");
console.Input.PushTextWithEnter("Banana");
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Banana")
.AddChoice("Orange")
.DefaultValue("Banana"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("AcceptChoice")]
public Task Should_Accept_Choice_In_List()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("Orange");
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Banana")
.AddChoice("Orange")
.DefaultValue("Banana"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("AutoComplete_Empty")]
public Task Should_Auto_Complete_To_First_Choice_If_Pressing_Tab_On_Empty_String()
{
// Given
var console = new TestConsole();
console.Input.PushKey(ConsoleKey.Tab);
console.Input.PushKey(ConsoleKey.Enter);
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Banana")
.AddChoice("Orange")
.DefaultValue("Banana"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("AutoComplete_BestMatch")]
public Task Should_Auto_Complete_To_Best_Match()
{
// Given
var console = new TestConsole();
console.Input.PushText("Band");
console.Input.PushKey(ConsoleKey.Tab);
console.Input.PushKey(ConsoleKey.Enter);
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Banana")
.AddChoice("Bandana")
.AddChoice("Orange"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("AutoComplete_NextChoice")]
public Task Should_Auto_Complete_To_Next_Choice_When_Pressing_Tab_On_A_Match()
{
// Given
var console = new TestConsole();
console.Input.PushText("Apple");
console.Input.PushKey(ConsoleKey.Tab);
console.Input.PushKey(ConsoleKey.Enter);
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Apple")
.AddChoice("Banana")
.AddChoice("Orange"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("CustomValidation")]
public Task Should_Return_Error_If_Custom_Validation_Fails()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("22");
console.Input.PushTextWithEnter("102");
console.Input.PushTextWithEnter("ABC");
console.Input.PushTextWithEnter("99");
// When
console.Prompt(
new TextPrompt<int>("Guess number:")
.ValidationErrorMessage("Invalid input")
.Validate(age =>
{
if (age < 99)
{
return ValidationResult.Error("Too low");
}
else if (age > 99)
{
return ValidationResult.Error("Too high");
}
return ValidationResult.Success();
}));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("CustomConverter")]
public Task Should_Use_Custom_Converter()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("Banana");
// When
var result = console.Prompt(
new TextPrompt<(int, string)>("Favorite fruit?")
.AddChoice((1, "Apple"))
.AddChoice((2, "Banana"))
.WithConverter(testData => testData.Item2));
// Then
result.Item1.ShouldBe(2);
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("SecretDefaultValue")]
public Task Should_Chose_Masked_Default_Value_If_Nothing_Is_Entered_And_Prompt_Is_Secret()
{
// Given
var console = new TestConsole();
console.Input.PushKey(ConsoleKey.Enter);
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.Secret()
.DefaultValue("Banana"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("NoSuffix")]
[GitHubIssue(413)]
public Task Should_Not_Append_Questionmark_Or_Colon_If_No_Choices_Are_Set()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("Orange");
// When
console.Prompt(
new TextPrompt<string>("Enter command$"));
// Then
return Verifier.Verify(console.Output);
}
// Then
result.ShouldBe("Hello World");
}
}
[Fact]
[Expectation("ConversionError")]
public Task Should_Return_Validation_Error_If_Value_Cannot_Be_Converted()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("ninety-nine");
console.Input.PushTextWithEnter("99");
// When
console.Prompt(new TextPrompt<int>("Age?"));
// Then
return Verifier.Verify(console.Lines);
}
[Fact]
[Expectation("DefaultValue")]
public Task Should_Chose_Default_Value_If_Nothing_Is_Entered()
{
// Given
var console = new TestConsole();
console.Input.PushKey(ConsoleKey.Enter);
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Banana")
.AddChoice("Orange")
.DefaultValue("Banana"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("InvalidChoice")]
public Task Should_Return_Error_If_An_Invalid_Choice_Is_Made()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("Apple");
console.Input.PushTextWithEnter("Banana");
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Banana")
.AddChoice("Orange")
.DefaultValue("Banana"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("AcceptChoice")]
public Task Should_Accept_Choice_In_List()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("Orange");
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Banana")
.AddChoice("Orange")
.DefaultValue("Banana"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("AutoComplete_Empty")]
public Task Should_Auto_Complete_To_First_Choice_If_Pressing_Tab_On_Empty_String()
{
// Given
var console = new TestConsole();
console.Input.PushKey(ConsoleKey.Tab);
console.Input.PushKey(ConsoleKey.Enter);
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Banana")
.AddChoice("Orange")
.DefaultValue("Banana"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("AutoComplete_BestMatch")]
public Task Should_Auto_Complete_To_Best_Match()
{
// Given
var console = new TestConsole();
console.Input.PushText("Band");
console.Input.PushKey(ConsoleKey.Tab);
console.Input.PushKey(ConsoleKey.Enter);
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Banana")
.AddChoice("Bandana")
.AddChoice("Orange"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("AutoComplete_NextChoice")]
public Task Should_Auto_Complete_To_Next_Choice_When_Pressing_Tab_On_A_Match()
{
// Given
var console = new TestConsole();
console.Input.PushText("Apple");
console.Input.PushKey(ConsoleKey.Tab);
console.Input.PushKey(ConsoleKey.Enter);
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Apple")
.AddChoice("Banana")
.AddChoice("Orange"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("CustomValidation")]
public Task Should_Return_Error_If_Custom_Validation_Fails()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("22");
console.Input.PushTextWithEnter("102");
console.Input.PushTextWithEnter("ABC");
console.Input.PushTextWithEnter("99");
// When
console.Prompt(
new TextPrompt<int>("Guess number:")
.ValidationErrorMessage("Invalid input")
.Validate(age =>
{
if (age < 99)
{
return ValidationResult.Error("Too low");
}
else if (age > 99)
{
return ValidationResult.Error("Too high");
}
return ValidationResult.Success();
}));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("CustomConverter")]
public Task Should_Use_Custom_Converter()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("Banana");
// When
var result = console.Prompt(
new TextPrompt<(int, string)>("Favorite fruit?")
.AddChoice((1, "Apple"))
.AddChoice((2, "Banana"))
.WithConverter(testData => testData.Item2));
// Then
result.Item1.ShouldBe(2);
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("SecretDefaultValue")]
public Task Should_Chose_Masked_Default_Value_If_Nothing_Is_Entered_And_Prompt_Is_Secret()
{
// Given
var console = new TestConsole();
console.Input.PushKey(ConsoleKey.Enter);
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.Secret()
.DefaultValue("Banana"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("NoSuffix")]
[GitHubIssue(413)]
public Task Should_Not_Append_Questionmark_Or_Colon_If_No_Choices_Are_Set()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("Orange");
// When
console.Prompt(
new TextPrompt<string>("Enter command$"));
// Then
return Verifier.Verify(console.Output);
}
}

View File

@ -1,54 +1,47 @@
using System.Threading.Tasks;
using Spectre.Console.Testing;
using Spectre.Verify.Extensions;
using VerifyXunit;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
[UsesVerify]
[ExpectationPath("Widgets/Recorder")]
public sealed class RecorderTests
{
[UsesVerify]
[ExpectationPath("Widgets/Recorder")]
public sealed class RecorderTests
[Fact]
[Expectation("Text")]
public Task Should_Export_Text_As_Expected()
{
[Fact]
[Expectation("Text")]
public Task Should_Export_Text_As_Expected()
{
// Given
var console = new TestConsole();
var recorder = new Recorder(console);
// Given
var console = new TestConsole();
var recorder = new Recorder(console);
recorder.Write(new Table()
.AddColumns("Foo", "Bar", "Qux")
.AddRow("Corgi", "Waldo", "Zap")
.AddRow(new Panel("Hello World").RoundedBorder()));
recorder.Write(new Table()
.AddColumns("Foo", "Bar", "Qux")
.AddRow("Corgi", "Waldo", "Zap")
.AddRow(new Panel("Hello World").RoundedBorder()));
// When
var result = recorder.ExportText();
// When
var result = recorder.ExportText();
// Then
return Verifier.Verify(result);
}
[Fact]
[Expectation("Html")]
public Task Should_Export_Html_Text_As_Expected()
{
// Given
var console = new TestConsole();
var recorder = new Recorder(console);
recorder.Write(new Table()
.AddColumns("[red on black]Foo[/]", "[green bold]Bar[/]", "[blue italic]Qux[/]")
.AddRow("[invert underline]Corgi[/]", "[bold strikethrough]Waldo[/]", "[dim]Zap[/]")
.AddRow(new Panel("[blue]Hello World[/]")
.BorderColor(Color.Red).RoundedBorder()));
// When
var result = recorder.ExportHtml();
// Then
return Verifier.Verify(result);
}
// Then
return Verifier.Verify(result);
}
}
[Fact]
[Expectation("Html")]
public Task Should_Export_Html_Text_As_Expected()
{
// Given
var console = new TestConsole();
var recorder = new Recorder(console);
recorder.Write(new Table()
.AddColumns("[red on black]Foo[/]", "[green bold]Bar[/]", "[blue italic]Qux[/]")
.AddRow("[invert underline]Corgi[/]", "[bold strikethrough]Waldo[/]", "[dim]Zap[/]")
.AddRow(new Panel("[blue]Hello World[/]")
.BorderColor(Color.Red).RoundedBorder()));
// When
var result = recorder.ExportHtml();
// Then
return Verifier.Verify(result);
}
}

View File

@ -1,210 +1,201 @@
using System.Threading.Tasks;
using Shouldly;
using Spectre.Console.Rendering;
using Spectre.Console.Testing;
using Spectre.Verify.Extensions;
using VerifyXunit;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
[UsesVerify]
[ExpectationPath("Rendering/Borders/Box")]
public sealed class BoxBorderTests
{
[UsesVerify]
[ExpectationPath("Rendering/Borders/Box")]
public sealed class BoxBorderTests
public sealed class NoBorder
{
[UsesVerify]
public sealed class NoBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxExtensions.GetSafeBorder(BoxBorder.None, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.None);
}
}
[Fact]
[Expectation("NoBorder")]
public Task Should_Render_As_Expected()
{
// Given
var console = new TestConsole();
var panel = Fixture.GetPanel().NoBorder();
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
}
[UsesVerify]
public sealed class AsciiBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxExtensions.GetSafeBorder(BoxBorder.Ascii, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Ascii);
}
}
[Fact]
[Expectation("AsciiBorder")]
public Task Should_Render_As_Expected()
{
// Given
var console = new TestConsole();
var panel = Fixture.GetPanel().AsciiBorder();
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
}
[UsesVerify]
public sealed class DoubleBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxExtensions.GetSafeBorder(BoxBorder.Double, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Double);
}
}
[Fact]
[Expectation("DoubleBorder")]
public Task Should_Render_As_Expected()
{
// Given
var console = new TestConsole();
var panel = Fixture.GetPanel().DoubleBorder();
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
}
[UsesVerify]
public sealed class HeavyBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxExtensions.GetSafeBorder(BoxBorder.Heavy, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
}
}
[Fact]
[Expectation("HeavyBorder")]
public Task Should_Render_As_Expected()
{
// Given
var console = new TestConsole();
var panel = Fixture.GetPanel().HeavyBorder();
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
}
[UsesVerify]
public sealed class RoundedBorder
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxExtensions.GetSafeBorder(BoxBorder.Rounded, safe: true);
var border = BoxExtensions.GetSafeBorder(BoxBorder.None, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
}
[Fact]
[Expectation("RoundedBorder")]
public Task Should_Render_As_Expected()
{
// Given
var console = new TestConsole();
var panel = Fixture.GetPanel().RoundedBorder();
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
border.ShouldBeSameAs(BoxBorder.None);
}
}
[UsesVerify]
public sealed class SquareBorder
[Fact]
[Expectation("NoBorder")]
public Task Should_Render_As_Expected()
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxExtensions.GetSafeBorder(BoxBorder.Square, safe: true);
// Given
var console = new TestConsole();
var panel = Fixture.GetPanel().NoBorder();
// Then
border.ShouldBeSameAs(BoxBorder.Square);
}
// When
console.Write(panel);
[Fact]
[Expectation("SquareBorder")]
public Task Should_Render_As_Expected()
{
// Given
var console = new TestConsole();
var panel = Fixture.GetPanel().SquareBorder();
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
}
private static class Fixture
{
public static Panel GetPanel()
{
return new Panel("Hello World")
.Header("Greeting");
}
// Then
return Verifier.Verify(console.Output);
}
}
}
[UsesVerify]
public sealed class AsciiBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxExtensions.GetSafeBorder(BoxBorder.Ascii, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Ascii);
}
}
[Fact]
[Expectation("AsciiBorder")]
public Task Should_Render_As_Expected()
{
// Given
var console = new TestConsole();
var panel = Fixture.GetPanel().AsciiBorder();
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
}
[UsesVerify]
public sealed class DoubleBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxExtensions.GetSafeBorder(BoxBorder.Double, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Double);
}
}
[Fact]
[Expectation("DoubleBorder")]
public Task Should_Render_As_Expected()
{
// Given
var console = new TestConsole();
var panel = Fixture.GetPanel().DoubleBorder();
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
}
[UsesVerify]
public sealed class HeavyBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxExtensions.GetSafeBorder(BoxBorder.Heavy, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
}
}
[Fact]
[Expectation("HeavyBorder")]
public Task Should_Render_As_Expected()
{
// Given
var console = new TestConsole();
var panel = Fixture.GetPanel().HeavyBorder();
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
}
[UsesVerify]
public sealed class RoundedBorder
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxExtensions.GetSafeBorder(BoxBorder.Rounded, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
}
[Fact]
[Expectation("RoundedBorder")]
public Task Should_Render_As_Expected()
{
// Given
var console = new TestConsole();
var panel = Fixture.GetPanel().RoundedBorder();
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
}
[UsesVerify]
public sealed class SquareBorder
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxExtensions.GetSafeBorder(BoxBorder.Square, safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
}
[Fact]
[Expectation("SquareBorder")]
public Task Should_Render_As_Expected()
{
// Given
var console = new TestConsole();
var panel = Fixture.GetPanel().SquareBorder();
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
}
private static class Fixture
{
public static Panel GetPanel()
{
return new Panel("Hello World")
.Header("Greeting");
}
}
}

View File

@ -1,35 +1,27 @@
using System.Collections.Generic;
using System.Linq;
using Shouldly;
using Spectre.Console.Rendering;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public sealed class RenderHookTests
{
public sealed class RenderHookTests
private sealed class HelloRenderHook : IRenderHook
{
private sealed class HelloRenderHook : IRenderHook
public IEnumerable<IRenderable> Process(RenderContext context, IEnumerable<IRenderable> renderables)
{
public IEnumerable<IRenderable> Process(RenderContext context, IEnumerable<IRenderable> renderables)
{
return new IRenderable[] { new Text("Hello\n") }.Concat(renderables);
}
}
[Fact]
public void Should_Inject_Renderable_Before_Writing_To_Console()
{
// Given
var console = new TestConsole();
console.Pipeline.Attach(new HelloRenderHook());
// When
console.Write(new Text("World"));
// Then
console.Lines[0].ShouldBe("Hello");
console.Lines[1].ShouldBe("World");
return new IRenderable[] { new Text("Hello\n") }.Concat(renderables);
}
}
}
[Fact]
public void Should_Inject_Renderable_Before_Writing_To_Console()
{
// Given
var console = new TestConsole();
console.Pipeline.Attach(new HelloRenderHook());
// When
console.Write(new Text("World"));
// Then
console.Lines[0].ShouldBe("Hello");
console.Lines[1].ShouldBe("World");
}
}

View File

@ -1,95 +1,90 @@
using Shouldly;
using Spectre.Console.Rendering;
using VerifyXunit;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public sealed class SegmentTests
{
public sealed class SegmentTests
[UsesVerify]
public sealed class TheSplitMethod
{
[UsesVerify]
public sealed class TheSplitMethod
[Theory]
[InlineData("Foo Bar", 0, "", "Foo Bar")]
[InlineData("Foo Bar", 1, "F", "oo Bar")]
[InlineData("Foo Bar", 2, "Fo", "o Bar")]
[InlineData("Foo Bar", 3, "Foo", " Bar")]
[InlineData("Foo Bar", 4, "Foo ", "Bar")]
[InlineData("Foo Bar", 5, "Foo B", "ar")]
[InlineData("Foo Bar", 6, "Foo Ba", "r")]
[InlineData("Foo Bar", 7, "Foo Bar", null)]
[InlineData("Foo 测试 Bar", 0, "", "Foo 测试 Bar")]
[InlineData("Foo 测试 Bar", 1, "F", "oo 测试 Bar")]
[InlineData("Foo 测试 Bar", 2, "Fo", "o 测试 Bar")]
[InlineData("Foo 测试 Bar", 3, "Foo", " 测试 Bar")]
[InlineData("Foo 测试 Bar", 4, "Foo ", "测试 Bar")]
[InlineData("Foo 测试 Bar", 5, "Foo 测", "试 Bar")]
[InlineData("Foo 测试 Bar", 6, "Foo 测", "试 Bar")]
[InlineData("Foo 测试 Bar", 7, "Foo 测试", " Bar")]
[InlineData("Foo 测试 Bar", 8, "Foo 测试", " Bar")]
[InlineData("Foo 测试 Bar", 9, "Foo 测试 ", "Bar")]
[InlineData("Foo 测试 Bar", 10, "Foo 测试 B", "ar")]
[InlineData("Foo 测试 Bar", 11, "Foo 测试 Ba", "r")]
[InlineData("Foo 测试 Bar", 12, "Foo 测试 Bar", null)]
public void Should_Split_Segment_Correctly(string text, int offset, string expectedFirst, string expectedSecond)
{
[Theory]
[InlineData("Foo Bar", 0, "", "Foo Bar")]
[InlineData("Foo Bar", 1, "F", "oo Bar")]
[InlineData("Foo Bar", 2, "Fo", "o Bar")]
[InlineData("Foo Bar", 3, "Foo", " Bar")]
[InlineData("Foo Bar", 4, "Foo ", "Bar")]
[InlineData("Foo Bar", 5, "Foo B", "ar")]
[InlineData("Foo Bar", 6, "Foo Ba", "r")]
[InlineData("Foo Bar", 7, "Foo Bar", null)]
[InlineData("Foo 测试 Bar", 0, "", "Foo 测试 Bar")]
[InlineData("Foo 测试 Bar", 1, "F", "oo 测试 Bar")]
[InlineData("Foo 测试 Bar", 2, "Fo", "o 测试 Bar")]
[InlineData("Foo 测试 Bar", 3, "Foo", " 测试 Bar")]
[InlineData("Foo 测试 Bar", 4, "Foo ", "测试 Bar")]
[InlineData("Foo 测试 Bar", 5, "Foo 测", "试 Bar")]
[InlineData("Foo 测试 Bar", 6, "Foo 测", "试 Bar")]
[InlineData("Foo 测试 Bar", 7, "Foo 测试", " Bar")]
[InlineData("Foo 测试 Bar", 8, "Foo 测试", " Bar")]
[InlineData("Foo 测试 Bar", 9, "Foo 测试 ", "Bar")]
[InlineData("Foo 测试 Bar", 10, "Foo 测试 B", "ar")]
[InlineData("Foo 测试 Bar", 11, "Foo 测试 Ba", "r")]
[InlineData("Foo 测试 Bar", 12, "Foo 测试 Bar", null)]
public void Should_Split_Segment_Correctly(string text, int offset, string expectedFirst, string expectedSecond)
{
// Given
var style = new Style(Color.Red, Color.Green, Decoration.Bold);
var segment = new Segment(text, style);
// Given
var style = new Style(Color.Red, Color.Green, Decoration.Bold);
var segment = new Segment(text, style);
// When
var (first, second) = segment.Split(offset);
// When
var (first, second) = segment.Split(offset);
// Then
first.Text.ShouldBe(expectedFirst);
first.Style.ShouldBe(style);
second?.Text?.ShouldBe(expectedSecond);
second?.Style?.ShouldBe(style);
}
// Then
first.Text.ShouldBe(expectedFirst);
first.Style.ShouldBe(style);
second?.Text?.ShouldBe(expectedSecond);
second?.Style?.ShouldBe(style);
}
}
[UsesVerify]
public sealed class TheSplitLinesMethod
{
[Fact]
public void Should_Split_Segment()
{
// Given, When
var lines = Segment.SplitLines(
new[]
{
new Segment("Foo"),
new Segment("Bar"),
new Segment("\n"),
new Segment("Baz"),
new Segment("Qux"),
new Segment("\n"),
new Segment("Corgi"),
});
// Then
lines.Count.ShouldBe(3);
lines[0].Count.ShouldBe(2);
lines[0][0].Text.ShouldBe("Foo");
lines[0][1].Text.ShouldBe("Bar");
lines[1].Count.ShouldBe(2);
lines[1][0].Text.ShouldBe("Baz");
lines[1][1].Text.ShouldBe("Qux");
lines[2].Count.ShouldBe(1);
lines[2][0].Text.ShouldBe("Corgi");
}
[UsesVerify]
public sealed class TheSplitLinesMethod
[Fact]
public void Should_Split_Segment_With_Windows_LineBreak()
{
[Fact]
public void Should_Split_Segment()
{
// Given, When
var lines = Segment.SplitLines(
new[]
{
new Segment("Foo"),
new Segment("Bar"),
new Segment("\n"),
new Segment("Baz"),
new Segment("Qux"),
new Segment("\n"),
new Segment("Corgi"),
});
// Then
lines.Count.ShouldBe(3);
lines[0].Count.ShouldBe(2);
lines[0][0].Text.ShouldBe("Foo");
lines[0][1].Text.ShouldBe("Bar");
lines[1].Count.ShouldBe(2);
lines[1][0].Text.ShouldBe("Baz");
lines[1][1].Text.ShouldBe("Qux");
lines[2].Count.ShouldBe(1);
lines[2][0].Text.ShouldBe("Corgi");
}
[Fact]
public void Should_Split_Segment_With_Windows_LineBreak()
{
// Given, When
var lines = Segment.SplitLines(
new[]
{
// Given, When
var lines = Segment.SplitLines(
new[]
{
new Segment("Foo"),
new Segment("Bar"),
new Segment("\r\n"),
@ -97,53 +92,52 @@ namespace Spectre.Console.Tests.Unit
new Segment("Qux"),
new Segment("\r\n"),
new Segment("Corgi"),
});
});
// Then
lines.Count.ShouldBe(3);
// Then
lines.Count.ShouldBe(3);
lines[0].Count.ShouldBe(2);
lines[0][0].Text.ShouldBe("Foo");
lines[0][1].Text.ShouldBe("Bar");
lines[0].Count.ShouldBe(2);
lines[0][0].Text.ShouldBe("Foo");
lines[0][1].Text.ShouldBe("Bar");
lines[1].Count.ShouldBe(2);
lines[1][0].Text.ShouldBe("Baz");
lines[1][1].Text.ShouldBe("Qux");
lines[1].Count.ShouldBe(2);
lines[1][0].Text.ShouldBe("Baz");
lines[1][1].Text.ShouldBe("Qux");
lines[2].Count.ShouldBe(1);
lines[2][0].Text.ShouldBe("Corgi");
}
lines[2].Count.ShouldBe(1);
lines[2][0].Text.ShouldBe("Corgi");
}
[Fact]
public void Should_Split_Segments_With_Linebreak_In_Text()
{
// Given, Given
var lines = Segment.SplitLines(
new[]
{
[Fact]
public void Should_Split_Segments_With_Linebreak_In_Text()
{
// Given, Given
var lines = Segment.SplitLines(
new[]
{
new Segment("Foo\n"),
new Segment("Bar\n"),
new Segment("Baz"),
new Segment("Qux\n"),
new Segment("Corgi"),
});
});
// Then
lines.Count.ShouldBe(4);
// Then
lines.Count.ShouldBe(4);
lines[0].Count.ShouldBe(1);
lines[0][0].Text.ShouldBe("Foo");
lines[0].Count.ShouldBe(1);
lines[0][0].Text.ShouldBe("Foo");
lines[1].Count.ShouldBe(1);
lines[1][0].Text.ShouldBe("Bar");
lines[1].Count.ShouldBe(1);
lines[1][0].Text.ShouldBe("Bar");
lines[2].Count.ShouldBe(2);
lines[2][0].Text.ShouldBe("Baz");
lines[2][1].Text.ShouldBe("Qux");
lines[2].Count.ShouldBe(2);
lines[2][0].Text.ShouldBe("Baz");
lines[2][1].Text.ShouldBe("Qux");
lines[3].Count.ShouldBe(1);
lines[3][0].Text.ShouldBe("Corgi");
}
lines[3].Count.ShouldBe(1);
lines[3][0].Text.ShouldBe("Corgi");
}
}
}
}

View File

@ -1,401 +1,396 @@
using System;
using Shouldly;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public sealed class StyleTests
{
public sealed class StyleTests
[Fact]
public void Should_Combine_Two_Styles_As_Expected()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic);
var other = new Style(Color.Green, Color.Silver, Decoration.Underline, "https://example.com");
// When
var result = first.Combine(other);
// Then
result.Foreground.ShouldBe(Color.Green);
result.Background.ShouldBe(Color.Silver);
result.Decoration.ShouldBe(Decoration.Bold | Decoration.Italic | Decoration.Underline);
result.Link.ShouldBe("https://example.com");
}
[Fact]
public void Should_Consider_Two_Identical_Styles_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeTrue();
}
[Fact]
public void Should_Not_Consider_Two_Styles_With_Different_Foreground_Colors_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.Blue, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeFalse();
}
[Fact]
public void Should_Not_Consider_Two_Styles_With_Different_Background_Colors_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.White, Color.Blue, Decoration.Bold | Decoration.Italic, "http://example.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeFalse();
}
[Fact]
public void Should_Not_Consider_Two_Styles_With_Different_Decorations_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.White, Color.Yellow, Decoration.Bold, "http://example.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeFalse();
}
[Fact]
public void Should_Not_Consider_Two_Styles_With_Different_Links_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://foo.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeFalse();
}
public sealed class TheParseMethod
{
[Fact]
public void Should_Combine_Two_Styles_As_Expected()
public void Default_Keyword_Should_Return_Default_Style()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic);
var other = new Style(Color.Green, Color.Silver, Decoration.Underline, "https://example.com");
// When
var result = first.Combine(other);
// Given, When
var result = Style.Parse("default");
// Then
result.Foreground.ShouldBe(Color.Green);
result.Background.ShouldBe(Color.Silver);
result.Decoration.ShouldBe(Decoration.Bold | Decoration.Italic | Decoration.Underline);
result.ShouldNotBeNull();
result.Foreground.ShouldBe(Color.Default);
result.Background.ShouldBe(Color.Default);
result.Decoration.ShouldBe(Decoration.None);
}
[Theory]
[InlineData("bold", Decoration.Bold)]
[InlineData("b", Decoration.Bold)]
[InlineData("dim", Decoration.Dim)]
[InlineData("i", Decoration.Italic)]
[InlineData("italic", Decoration.Italic)]
[InlineData("underline", Decoration.Underline)]
[InlineData("u", Decoration.Underline)]
[InlineData("invert", Decoration.Invert)]
[InlineData("conceal", Decoration.Conceal)]
[InlineData("slowblink", Decoration.SlowBlink)]
[InlineData("rapidblink", Decoration.RapidBlink)]
[InlineData("strikethrough", Decoration.Strikethrough)]
[InlineData("s", Decoration.Strikethrough)]
public void Should_Parse_Decoration(string text, Decoration decoration)
{
// Given, When
var result = Style.Parse(text);
// Then
result.ShouldNotBeNull();
result.Decoration.ShouldBe(decoration);
}
[Fact]
public void Should_Parse_Link_Without_Address()
{
// Given, When
var result = Style.Parse("link");
// Then
result.ShouldNotBeNull();
result.Link.ShouldBe("https://emptylink");
}
[Fact]
public void Should_Parse_Link()
{
// Given, When
var result = Style.Parse("link=https://example.com");
// Then
result.ShouldNotBeNull();
result.Link.ShouldBe("https://example.com");
}
[Fact]
public void Should_Consider_Two_Identical_Styles_Equal()
public void Should_Throw_If_Link_Is_Set_Twice()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
// Given, When
var result = Record.Exception(() => Style.Parse("link=https://example.com link=https://example.com"));
// When
var result = first.Equals(second);
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("A link has already been set.");
}
[Fact]
public void Should_Parse_Background_If_Foreground_Is_Set_To_Default()
{
// Given, When
var result = Style.Parse("default on green");
// Then
result.ShouldNotBeNull();
result.Decoration.ShouldBe(Decoration.None);
result.Foreground.ShouldBe(Color.Default);
result.Background.ShouldBe(Color.Green);
}
[Fact]
public void Should_Throw_If_Foreground_Is_Set_Twice()
{
// Given, When
var result = Record.Exception(() => Style.Parse("green yellow"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("A foreground color has already been set.");
}
[Fact]
public void Should_Throw_If_Background_Is_Set_Twice()
{
// Given, When
var result = Record.Exception(() => Style.Parse("green on blue yellow"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("A background color has already been set.");
}
[Fact]
public void Should_Throw_If_Color_Name_Could_Not_Be_Found()
{
// Given, When
var result = Record.Exception(() => Style.Parse("bold lol"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("Could not find color or style 'lol'.");
}
[Fact]
public void Should_Throw_If_Background_Color_Name_Could_Not_Be_Found()
{
// Given, When
var result = Record.Exception(() => Style.Parse("blue on lol"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("Could not find color 'lol'.");
}
[Fact]
public void Should_Parse_Colors_And_Decoration_And_Link()
{
// Given, When
var result = Style.Parse("link=https://example.com bold underline blue on green");
// Then
result.ShouldNotBeNull();
result.Decoration.ShouldBe(Decoration.Bold | Decoration.Underline);
result.Foreground.ShouldBe(Color.Blue);
result.Background.ShouldBe(Color.Green);
result.Link.ShouldBe("https://example.com");
}
[Theory]
[InlineData("#FF0000 on #0000FF")]
[InlineData("#F00 on #00F")]
public void Should_Parse_Hex_Colors_Correctly(string style)
{
// Given, When
var result = Style.Parse(style);
// Then
result.Foreground.ShouldBe(Color.Red);
result.Background.ShouldBe(Color.Blue);
}
[Theory]
[InlineData("#", "Invalid hex color '#'.")]
[InlineData("#FF00FF00FF", "Invalid hex color '#FF00FF00FF'.")]
[InlineData("#FOO", "Invalid hex color '#FOO'. Could not find any recognizable digits.")]
public void Should_Return_Error_If_Hex_Color_Is_Invalid(string style, string expected)
{
// Given, When
var result = Record.Exception(() => Style.Parse(style));
// Then
result.ShouldNotBeNull();
result.Message.ShouldBe(expected);
}
[Theory]
[InlineData("rgb(255,0,0) on rgb(0,0,255)")]
public void Should_Parse_Rgb_Colors_Correctly(string style)
{
// Given, When
var result = Style.Parse(style);
// Then
result.Foreground.ShouldBe(Color.Red);
result.Background.ShouldBe(Color.Blue);
}
[Theory]
[InlineData("12 on 24")]
public void Should_Parse_Colors_Numbers_Correctly(string style)
{
// Given, When
var result = Style.Parse(style);
// Then
result.Foreground.ShouldBe(Color.Blue);
result.Background.ShouldBe(Color.DeepSkyBlue4_1);
}
[Theory]
[InlineData("-12", "Color number must be greater than or equal to 0 (was -12)")]
[InlineData("256", "Color number must be less than or equal to 255 (was 256)")]
public void Should_Return_Error_If_Color_Number_Is_Invalid(string style, string expected)
{
// Given, When
var result = Record.Exception(() => Style.Parse(style));
// Then
result.ShouldNotBeNull();
result.Message.ShouldBe(expected);
}
[Theory]
[InlineData("rgb()", "Invalid RGB color 'rgb()'.")]
[InlineData("rgb(", "Invalid RGB color 'rgb('.")]
[InlineData("rgb(255)", "Invalid RGB color 'rgb(255)'.")]
[InlineData("rgb(255,255)", "Invalid RGB color 'rgb(255,255)'.")]
[InlineData("rgb(255,255,255", "Invalid RGB color 'rgb(255,255,255'.")]
[InlineData("rgb(A,B,C)", "Invalid RGB color 'rgb(A,B,C)'. Input string was not in a correct format.")]
public void Should_Return_Error_If_Rgb_Color_Is_Invalid(string style, string expected)
{
// Given, When
var result = Record.Exception(() => Style.Parse(style));
// Then
result.ShouldNotBeNull();
result.Message.ShouldBe(expected);
}
}
public sealed class TheTryParseMethod
{
[Fact]
public void Should_Return_True_If_Parsing_Succeeded()
{
// Given, When
var result = Style.TryParse("bold", out var style);
// Then
result.ShouldBeTrue();
style.ShouldNotBeNull();
style.Decoration.ShouldBe(Decoration.Bold);
}
[Fact]
public void Should_Not_Consider_Two_Styles_With_Different_Foreground_Colors_Equal()
public void Should_Return_False_If_Parsing_Failed()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.Blue, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
// When
var result = first.Equals(second);
// Given, When
var result = Style.TryParse("lol", out _);
// Then
result.ShouldBeFalse();
}
[Fact]
public void Should_Not_Consider_Two_Styles_With_Different_Background_Colors_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.White, Color.Blue, Decoration.Bold | Decoration.Italic, "http://example.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeFalse();
}
[Fact]
public void Should_Not_Consider_Two_Styles_With_Different_Decorations_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.White, Color.Yellow, Decoration.Bold, "http://example.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeFalse();
}
[Fact]
public void Should_Not_Consider_Two_Styles_With_Different_Links_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://foo.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeFalse();
}
public sealed class TheParseMethod
{
[Fact]
public void Default_Keyword_Should_Return_Default_Style()
{
// Given, When
var result = Style.Parse("default");
// Then
result.ShouldNotBeNull();
result.Foreground.ShouldBe(Color.Default);
result.Background.ShouldBe(Color.Default);
result.Decoration.ShouldBe(Decoration.None);
}
[Theory]
[InlineData("bold", Decoration.Bold)]
[InlineData("b", Decoration.Bold)]
[InlineData("dim", Decoration.Dim)]
[InlineData("i", Decoration.Italic)]
[InlineData("italic", Decoration.Italic)]
[InlineData("underline", Decoration.Underline)]
[InlineData("u", Decoration.Underline)]
[InlineData("invert", Decoration.Invert)]
[InlineData("conceal", Decoration.Conceal)]
[InlineData("slowblink", Decoration.SlowBlink)]
[InlineData("rapidblink", Decoration.RapidBlink)]
[InlineData("strikethrough", Decoration.Strikethrough)]
[InlineData("s", Decoration.Strikethrough)]
public void Should_Parse_Decoration(string text, Decoration decoration)
{
// Given, When
var result = Style.Parse(text);
// Then
result.ShouldNotBeNull();
result.Decoration.ShouldBe(decoration);
}
[Fact]
public void Should_Parse_Link_Without_Address()
{
// Given, When
var result = Style.Parse("link");
// Then
result.ShouldNotBeNull();
result.Link.ShouldBe("https://emptylink");
}
[Fact]
public void Should_Parse_Link()
{
// Given, When
var result = Style.Parse("link=https://example.com");
// Then
result.ShouldNotBeNull();
result.Link.ShouldBe("https://example.com");
}
[Fact]
public void Should_Throw_If_Link_Is_Set_Twice()
{
// Given, When
var result = Record.Exception(() => Style.Parse("link=https://example.com link=https://example.com"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("A link has already been set.");
}
[Fact]
public void Should_Parse_Background_If_Foreground_Is_Set_To_Default()
{
// Given, When
var result = Style.Parse("default on green");
// Then
result.ShouldNotBeNull();
result.Decoration.ShouldBe(Decoration.None);
result.Foreground.ShouldBe(Color.Default);
result.Background.ShouldBe(Color.Green);
}
[Fact]
public void Should_Throw_If_Foreground_Is_Set_Twice()
{
// Given, When
var result = Record.Exception(() => Style.Parse("green yellow"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("A foreground color has already been set.");
}
[Fact]
public void Should_Throw_If_Background_Is_Set_Twice()
{
// Given, When
var result = Record.Exception(() => Style.Parse("green on blue yellow"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("A background color has already been set.");
}
[Fact]
public void Should_Throw_If_Color_Name_Could_Not_Be_Found()
{
// Given, When
var result = Record.Exception(() => Style.Parse("bold lol"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("Could not find color or style 'lol'.");
}
[Fact]
public void Should_Throw_If_Background_Color_Name_Could_Not_Be_Found()
{
// Given, When
var result = Record.Exception(() => Style.Parse("blue on lol"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("Could not find color 'lol'.");
}
[Fact]
public void Should_Parse_Colors_And_Decoration_And_Link()
{
// Given, When
var result = Style.Parse("link=https://example.com bold underline blue on green");
// Then
result.ShouldNotBeNull();
result.Decoration.ShouldBe(Decoration.Bold | Decoration.Underline);
result.Foreground.ShouldBe(Color.Blue);
result.Background.ShouldBe(Color.Green);
result.Link.ShouldBe("https://example.com");
}
[Theory]
[InlineData("#FF0000 on #0000FF")]
[InlineData("#F00 on #00F")]
public void Should_Parse_Hex_Colors_Correctly(string style)
{
// Given, When
var result = Style.Parse(style);
// Then
result.Foreground.ShouldBe(Color.Red);
result.Background.ShouldBe(Color.Blue);
}
[Theory]
[InlineData("#", "Invalid hex color '#'.")]
[InlineData("#FF00FF00FF", "Invalid hex color '#FF00FF00FF'.")]
[InlineData("#FOO", "Invalid hex color '#FOO'. Could not find any recognizable digits.")]
public void Should_Return_Error_If_Hex_Color_Is_Invalid(string style, string expected)
{
// Given, When
var result = Record.Exception(() => Style.Parse(style));
// Then
result.ShouldNotBeNull();
result.Message.ShouldBe(expected);
}
[Theory]
[InlineData("rgb(255,0,0) on rgb(0,0,255)")]
public void Should_Parse_Rgb_Colors_Correctly(string style)
{
// Given, When
var result = Style.Parse(style);
// Then
result.Foreground.ShouldBe(Color.Red);
result.Background.ShouldBe(Color.Blue);
}
[Theory]
[InlineData("12 on 24")]
public void Should_Parse_Colors_Numbers_Correctly(string style)
{
// Given, When
var result = Style.Parse(style);
// Then
result.Foreground.ShouldBe(Color.Blue);
result.Background.ShouldBe(Color.DeepSkyBlue4_1);
}
[Theory]
[InlineData("-12", "Color number must be greater than or equal to 0 (was -12)")]
[InlineData("256", "Color number must be less than or equal to 255 (was 256)")]
public void Should_Return_Error_If_Color_Number_Is_Invalid(string style, string expected)
{
// Given, When
var result = Record.Exception(() => Style.Parse(style));
// Then
result.ShouldNotBeNull();
result.Message.ShouldBe(expected);
}
[Theory]
[InlineData("rgb()", "Invalid RGB color 'rgb()'.")]
[InlineData("rgb(", "Invalid RGB color 'rgb('.")]
[InlineData("rgb(255)", "Invalid RGB color 'rgb(255)'.")]
[InlineData("rgb(255,255)", "Invalid RGB color 'rgb(255,255)'.")]
[InlineData("rgb(255,255,255", "Invalid RGB color 'rgb(255,255,255'.")]
[InlineData("rgb(A,B,C)", "Invalid RGB color 'rgb(A,B,C)'. Input string was not in a correct format.")]
public void Should_Return_Error_If_Rgb_Color_Is_Invalid(string style, string expected)
{
// Given, When
var result = Record.Exception(() => Style.Parse(style));
// Then
result.ShouldNotBeNull();
result.Message.ShouldBe(expected);
}
}
public sealed class TheTryParseMethod
{
[Fact]
public void Should_Return_True_If_Parsing_Succeeded()
{
// Given, When
var result = Style.TryParse("bold", out var style);
// Then
result.ShouldBeTrue();
style.ShouldNotBeNull();
style.Decoration.ShouldBe(Decoration.Bold);
}
[Fact]
public void Should_Return_False_If_Parsing_Failed()
{
// Given, When
var result = Style.TryParse("lol", out _);
// Then
result.ShouldBeFalse();
}
}
public sealed class TheToMarkupMethod
{
[Fact]
public void Should_Return_Expected_Markup_For_Style_With_Foreground_Color()
{
// Given
var style = new Style(Color.Red);
// When
var result = style.ToMarkup();
// Then
result.ShouldBe("red");
}
[Fact]
public void Should_Return_Expected_Markup_For_Style_With_Foreground_And_Background_Color()
{
// Given
var style = new Style(Color.Red, Color.Green);
// When
var result = style.ToMarkup();
// Then
result.ShouldBe("red on green");
}
[Fact]
public void Should_Return_Expected_Markup_For_Style_With_Foreground_And_Background_Color_And_Decoration()
{
// Given
var style = new Style(Color.Red, Color.Green, Decoration.Bold | Decoration.Underline);
// When
var result = style.ToMarkup();
// Then
result.ShouldBe("bold underline red on green");
}
[Fact]
public void Should_Return_Expected_Markup_For_Style_With_Only_Background_Color()
{
// Given
var style = new Style(background: Color.Green);
// When
var result = style.ToMarkup();
// Then
result.ShouldBe("default on green");
}
}
}
}
public sealed class TheToMarkupMethod
{
[Fact]
public void Should_Return_Expected_Markup_For_Style_With_Foreground_Color()
{
// Given
var style = new Style(Color.Red);
// When
var result = style.ToMarkup();
// Then
result.ShouldBe("red");
}
[Fact]
public void Should_Return_Expected_Markup_For_Style_With_Foreground_And_Background_Color()
{
// Given
var style = new Style(Color.Red, Color.Green);
// When
var result = style.ToMarkup();
// Then
result.ShouldBe("red on green");
}
[Fact]
public void Should_Return_Expected_Markup_For_Style_With_Foreground_And_Background_Color_And_Decoration()
{
// Given
var style = new Style(Color.Red, Color.Green, Decoration.Bold | Decoration.Underline);
// When
var result = style.ToMarkup();
// Then
result.ShouldBe("bold underline red on green");
}
[Fact]
public void Should_Return_Expected_Markup_For_Style_With_Only_Background_Color()
{
// Given
var style = new Style(background: Color.Green);
// When
var result = style.ToMarkup();
// Then
result.ShouldBe("default on green");
}
}
}

Some files were not shown because too many files have changed in this diff Show More