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

View File

@ -1,72 +1,64 @@
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/BarChart")]
public sealed class BarChartTests
{
[UsesVerify]
[ExpectationPath("Widgets/BarChart")]
public sealed class BarChartTests
[Fact]
[Expectation("Render")]
public async Task Should_Render_Correctly()
{
[Fact]
[Expectation("Render")]
public async Task Should_Render_Correctly()
{
// Given
var console = new TestConsole();
// Given
var console = new TestConsole();
// When
console.Write(new BarChart()
.Width(60)
.Label("Number of fruits")
.AddItem("Apple", 12)
.AddItem("Orange", 54)
.AddItem("Banana", 33));
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Zero_Value")]
public async Task Should_Render_Correctly_2()
{
// Given
var console = new TestConsole();
// When
console.Write(new BarChart()
.Width(60)
.Label("Number of fruits")
.AddItem("Apple", 0)
.AddItem("Orange", 54)
.AddItem("Banana", 33));
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Fixed_Max_Value")]
public async Task Should_Render_Correctly_3()
{
// Given
var console = new TestConsole();
// When
console.Write(new BarChart()
.Width(60)
.WithMaxValue(100)
.Label("Number of fruits")
.AddItem("Apple", 12)
.AddItem("Orange", 54)
.AddItem("Banana", 33));
// Then
await Verifier.Verify(console.Output);
}
// When
console.Write(new BarChart()
.Width(60)
.Label("Number of fruits")
.AddItem("Apple", 12)
.AddItem("Orange", 54)
.AddItem("Banana", 33));
// Then
await Verifier.Verify(console.Output);
}
}
[Fact]
[Expectation("Zero_Value")]
public async Task Should_Render_Correctly_2()
{
// Given
var console = new TestConsole();
// When
console.Write(new BarChart()
.Width(60)
.Label("Number of fruits")
.AddItem("Apple", 0)
.AddItem("Orange", 54)
.AddItem("Banana", 33));
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Fixed_Max_Value")]
public async Task Should_Render_Correctly_3()
{
// Given
var console = new TestConsole();
// When
console.Write(new BarChart()
.Width(60)
.WithMaxValue(100)
.Label("Number of fruits")
.AddItem("Apple", 12)
.AddItem("Orange", 54)
.AddItem("Banana", 33));
// Then
await Verifier.Verify(console.Output);
}
}

View File

@ -1,150 +1,143 @@
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/BreakdownChart")]
public sealed class BreakdownChartTests
{
[UsesVerify]
[ExpectationPath("Widgets/BreakdownChart")]
public sealed class BreakdownChartTests
[Fact]
[Expectation("Default")]
public async Task Should_Render_Correctly()
{
[Fact]
[Expectation("Default")]
public async Task Should_Render_Correctly()
// Given
var console = new TestConsole();
var chart = Fixture.GetChart();
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Width")]
public async Task Should_Render_With_Specific_Width()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart().Width(60);
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("TagFormat")]
public async Task Should_Render_Correctly_With_Specific_Value_Formatter()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart()
.Width(60)
.Culture("sv-SE")
.UseValueFormatter((v, c) => string.Format(c, "{0}%", v));
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("HideTags")]
public async Task Should_Render_Correctly_Without_Tags()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart().Width(60).HideTags();
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("HideTagValues")]
public async Task Should_Render_Correctly_Without_Tag_Values()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart().Width(60).HideTagValues();
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Culture")]
public async Task Should_Render_Correctly_With_Specific_Culture()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart().Width(60).Culture("sv-SE");
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("FullSize")]
public async Task Should_Render_FullSize_Mode_Correctly()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart().Width(60).FullSize();
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Ansi")]
public async Task Should_Render_Correct_Ansi()
{
// Given
var console = new TestConsole().EmitAnsiSequences();
var chart = Fixture.GetChart().Width(60).FullSize();
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
public static class Fixture
{
public static BreakdownChart GetChart()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart();
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Width")]
public async Task Should_Render_With_Specific_Width()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart().Width(60);
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("TagFormat")]
public async Task Should_Render_Correctly_With_Specific_Value_Formatter()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart()
.Width(60)
.Culture("sv-SE")
.UseValueFormatter((v, c) => string.Format(c, "{0}%", v));
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("HideTags")]
public async Task Should_Render_Correctly_Without_Tags()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart().Width(60).HideTags();
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("HideTagValues")]
public async Task Should_Render_Correctly_Without_Tag_Values()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart().Width(60).HideTagValues();
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Culture")]
public async Task Should_Render_Correctly_With_Specific_Culture()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart().Width(60).Culture("sv-SE");
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("FullSize")]
public async Task Should_Render_FullSize_Mode_Correctly()
{
// Given
var console = new TestConsole();
var chart = Fixture.GetChart().Width(60).FullSize();
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Ansi")]
public async Task Should_Render_Correct_Ansi()
{
// Given
var console = new TestConsole().EmitAnsiSequences();
var chart = Fixture.GetChart().Width(60).FullSize();
// When
console.Write(chart);
// Then
await Verifier.Verify(console.Output);
}
public static class Fixture
{
public static BreakdownChart GetChart()
{
return new BreakdownChart()
.AddItem("SCSS", 37, Color.Red)
.AddItem("HTML", 28.3, Color.Blue)
.AddItem("C#", 22.6, Color.Green)
.AddItem("JavaScript", 6, Color.Yellow)
.AddItem("Ruby", 6, Color.LightGreen)
.AddItem("Shell", 0.1, Color.Aqua);
}
return new BreakdownChart()
.AddItem("SCSS", 37, Color.Red)
.AddItem("HTML", 28.3, Color.Blue)
.AddItem("C#", 22.6, Color.Green)
.AddItem("JavaScript", 6, Color.Yellow)
.AddItem("Ruby", 6, Color.LightGreen)
.AddItem("Shell", 0.1, Color.Aqua);
}
}
}
}

View File

@ -1,108 +1,100 @@
using System;
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/Calendar")]
public sealed class CalendarTests
{
[UsesVerify]
[ExpectationPath("Widgets/Calendar")]
public sealed class CalendarTests
[Fact]
[Expectation("Render")]
public Task Should_Render_Calendar_Correctly()
{
[Fact]
[Expectation("Render")]
public Task Should_Render_Calendar_Correctly()
{
// Given
var console = new TestConsole();
var calendar = new Calendar(2020, 10)
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// Given
var console = new TestConsole();
var calendar = new Calendar(2020, 10)
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Write(calendar);
// When
console.Write(calendar);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Centered")]
public Task Should_Center_Calendar_Correctly()
{
// Given
var console = new TestConsole();
var calendar = new Calendar(2020, 10)
.Centered()
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Write(calendar);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("LeftAligned")]
public Task Should_Left_Align_Calendar_Correctly()
{
// Given
var console = new TestConsole();
var calendar = new Calendar(2020, 10)
.LeftAligned()
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Write(calendar);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("RightAligned")]
public Task Should_Right_Align_Calendar_Correctly()
{
// Given
var console = new TestConsole();
var calendar = new Calendar(2020, 10)
.RightAligned()
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Write(calendar);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Culture")]
public Task Should_Render_Calendar_Correctly_For_Specific_Culture()
{
// Given
var console = new TestConsole();
var calendar = new Calendar(2020, 10, 15)
.Culture("de-DE")
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Write(calendar);
// Then
return Verifier.Verify(console.Output);
}
// Then
return Verifier.Verify(console.Output);
}
}
[Fact]
[Expectation("Centered")]
public Task Should_Center_Calendar_Correctly()
{
// Given
var console = new TestConsole();
var calendar = new Calendar(2020, 10)
.Centered()
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Write(calendar);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("LeftAligned")]
public Task Should_Left_Align_Calendar_Correctly()
{
// Given
var console = new TestConsole();
var calendar = new Calendar(2020, 10)
.LeftAligned()
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Write(calendar);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("RightAligned")]
public Task Should_Right_Align_Calendar_Correctly()
{
// Given
var console = new TestConsole();
var calendar = new Calendar(2020, 10)
.RightAligned()
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Write(calendar);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Culture")]
public Task Should_Render_Calendar_Correctly_For_Specific_Culture()
{
// Given
var console = new TestConsole();
var calendar = new Calendar(2020, 10, 15)
.Culture("de-DE")
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Write(calendar);
// Then
return Verifier.Verify(console.Output);
}
}

View File

@ -1,143 +1,134 @@
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("Widgets/Canvas")]
public class CanvasTests
{
[UsesVerify]
[ExpectationPath("Widgets/Canvas")]
public class CanvasTests
public sealed class TheConstructor
{
public sealed class TheConstructor
[Fact]
public void Should_Throw_If_Width_Is_Less_Than_Zero()
{
[Fact]
public void Should_Throw_If_Width_Is_Less_Than_Zero()
{
// Given, When
var result = Record.Exception(() => new Canvas(0, 1));
// Given, When
var result = Record.Exception(() => new Canvas(0, 1));
// Then
result.ShouldBeOfType<ArgumentException>()
.And(ex => ex.ParamName.ShouldBe("width"));
}
[Fact]
public void Should_Throw_If_Height_Is_Less_Than_Zero()
{
// Given, When
var result = Record.Exception(() => new Canvas(1, 0));
// Then
result.ShouldBeOfType<ArgumentException>()
.And(ex => ex.ParamName.ShouldBe("height"));
}
// Then
result.ShouldBeOfType<ArgumentException>()
.And(ex => ex.ParamName.ShouldBe("width"));
}
[Fact]
[Expectation("Render")]
public async Task Should_Render_Canvas_Correctly()
public void Should_Throw_If_Height_Is_Less_Than_Zero()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
var canvas = new Canvas(width: 5, height: 5);
canvas.SetPixel(0, 0, Color.Red);
canvas.SetPixel(4, 0, Color.Green);
canvas.SetPixel(0, 4, Color.Blue);
canvas.SetPixel(4, 4, Color.Yellow);
// When
console.Write(canvas);
// Given, When
var result = Record.Exception(() => new Canvas(1, 0));
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Nested")]
public async Task Simple_Measure()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
var panel = new Panel(new Canvas(width: 2, height: 2)
.SetPixel(0, 0, Color.Aqua)
.SetPixel(1, 1, Color.Grey));
// When
console.Write(panel);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_NarrowTerminal")]
public async Task Should_Scale_Down_Canvas_Is_Bigger_Than_Terminal()
{
// Given
var console = new TestConsole()
.Width(10)
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
var canvas = new Canvas(width: 20, height: 10);
canvas.SetPixel(0, 0, Color.Aqua);
canvas.SetPixel(19, 9, Color.Grey);
// When
console.Write(canvas);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_MaxWidth")]
public async Task Should_Scale_Down_Canvas_If_MaxWidth_Is_Set()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
var canvas = new Canvas(width: 20, height: 10) { MaxWidth = 10 };
canvas.SetPixel(0, 0, Color.Aqua);
canvas.SetPixel(19, 9, Color.Aqua);
// When
console.Write(canvas);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
public void Should_Not_Render_Canvas_If_Canvas_Cannot_Be_Scaled_Down()
{
// Given
var console = new TestConsole()
.Width(10)
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
var canvas = new Canvas(width: 20, height: 2);
canvas.SetPixel(0, 0, Color.Aqua);
canvas.SetPixel(19, 1, Color.Grey);
// When
console.Write(canvas);
// Then
console.Output.ShouldBeEmpty();
result.ShouldBeOfType<ArgumentException>()
.And(ex => ex.ParamName.ShouldBe("height"));
}
}
}
[Fact]
[Expectation("Render")]
public async Task Should_Render_Canvas_Correctly()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
var canvas = new Canvas(width: 5, height: 5);
canvas.SetPixel(0, 0, Color.Red);
canvas.SetPixel(4, 0, Color.Green);
canvas.SetPixel(0, 4, Color.Blue);
canvas.SetPixel(4, 4, Color.Yellow);
// When
console.Write(canvas);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Nested")]
public async Task Simple_Measure()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
var panel = new Panel(new Canvas(width: 2, height: 2)
.SetPixel(0, 0, Color.Aqua)
.SetPixel(1, 1, Color.Grey));
// When
console.Write(panel);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_NarrowTerminal")]
public async Task Should_Scale_Down_Canvas_Is_Bigger_Than_Terminal()
{
// Given
var console = new TestConsole()
.Width(10)
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
var canvas = new Canvas(width: 20, height: 10);
canvas.SetPixel(0, 0, Color.Aqua);
canvas.SetPixel(19, 9, Color.Grey);
// When
console.Write(canvas);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_MaxWidth")]
public async Task Should_Scale_Down_Canvas_If_MaxWidth_Is_Set()
{
// Given
var console = new TestConsole()
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
var canvas = new Canvas(width: 20, height: 10) { MaxWidth = 10 };
canvas.SetPixel(0, 0, Color.Aqua);
canvas.SetPixel(19, 9, Color.Aqua);
// When
console.Write(canvas);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
public void Should_Not_Render_Canvas_If_Canvas_Cannot_Be_Scaled_Down()
{
// Given
var console = new TestConsole()
.Width(10)
.Colors(ColorSystem.Standard)
.EmitAnsiSequences();
var canvas = new Canvas(width: 20, height: 2);
canvas.SetPixel(0, 0, Color.Aqua);
canvas.SetPixel(19, 1, Color.Grey);
// When
console.Write(canvas);
// Then
console.Output.ShouldBeEmpty();
}
}

View File

@ -1,48 +1,40 @@
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("Widgets/Columns")]
public sealed class ColumnsTests
{
[UsesVerify]
[ExpectationPath("Widgets/Columns")]
public sealed class ColumnsTests
private sealed class User
{
private sealed class User
{
public string Name { get; set; }
public string Country { get; set; }
}
[Fact]
[Expectation("Render")]
public Task Should_Render_Columns_Correctly()
{
// Given
var console = new TestConsole().Width(61);
var users = new[]
{
new User { Name = "Savannah Thompson", Country = "Australia" },
new User { Name = "Sophie Ramos", Country = "United States" },
new User { Name = "Katrin Goldberg", Country = "Germany" },
};
var cards = new List<Panel>();
foreach (var user in users)
{
cards.Add(
new Panel($"[b]{user.Name}[/]\n[yellow]{user.Country}[/]")
.RoundedBorder().Expand());
}
// When
console.Write(new Columns(cards));
// Then
return Verifier.Verify(console.Output);
}
public string Name { get; set; }
public string Country { get; set; }
}
}
[Fact]
[Expectation("Render")]
public Task Should_Render_Columns_Correctly()
{
// Given
var console = new TestConsole().Width(61);
var users = new[]
{
new User { Name = "Savannah Thompson", Country = "Australia" },
new User { Name = "Sophie Ramos", Country = "United States" },
new User { Name = "Katrin Goldberg", Country = "Germany" },
};
var cards = new List<Panel>();
foreach (var user in users)
{
cards.Add(
new Panel($"[b]{user.Name}[/]\n[yellow]{user.Country}[/]")
.RoundedBorder().Expand());
}
// When
console.Write(new Columns(cards));
// Then
return Verifier.Verify(console.Output);
}
}

View File

@ -1,107 +1,100 @@
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/Figlet")]
public sealed class FigletTests
{
[UsesVerify]
[ExpectationPath("Widgets/Figlet")]
public sealed class FigletTests
[Fact]
[Expectation("Load_Stream")]
public async Task Should_Load_Font_From_Stream()
{
[Fact]
[Expectation("Load_Stream")]
public async Task Should_Load_Font_From_Stream()
{
// Given
var console = new TestConsole().Width(180);
var font = FigletFont.Load(EmbeddedResourceReader.LoadResourceStream("Spectre.Console.Tests/Data/starwars.flf"));
var text = new FigletText(font, "Patrik was here");
// Given
var console = new TestConsole().Width(180);
var font = FigletFont.Load(EmbeddedResourceReader.LoadResourceStream("Spectre.Console.Tests/Data/starwars.flf"));
var text = new FigletText(font, "Patrik was here");
// When
console.Write(text);
// When
console.Write(text);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render")]
public async Task Should_Render_Text_Correctly()
{
// Given
var console = new TestConsole().Width(70);
var text = new FigletText(FigletFont.Default, "Patrik was here");
// When
console.Write(text);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Wrapped")]
public async Task Should_Render_Wrapped_Text_Correctly()
{
// Given
var console = new TestConsole().Width(70);
var text = new FigletText(FigletFont.Default, "Spectre.Console");
// When
console.Write(text);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_LeftAligned")]
public async Task Should_Render_Left_Aligned_Text_Correctly()
{
// Given
var console = new TestConsole().Width(120);
var text = new FigletText(FigletFont.Default, "Spectre.Console")
.Alignment(Justify.Left);
// When
console.Write(text);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Centered")]
public async Task Should_Render_Centered_Text_Correctly()
{
// Given
var console = new TestConsole().Width(120);
var text = new FigletText(FigletFont.Default, "Spectre.Console")
.Alignment(Justify.Center);
// When
console.Write(text);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_RightAligned")]
public async Task Should_Render_Right_Aligned_Text_Correctly()
{
// Given
var console = new TestConsole().Width(120);
var text = new FigletText(FigletFont.Default, "Spectre.Console")
.Alignment(Justify.Right);
// When
console.Write(text);
// Then
await Verifier.Verify(console.Output);
}
// Then
await Verifier.Verify(console.Output);
}
}
[Fact]
[Expectation("Render")]
public async Task Should_Render_Text_Correctly()
{
// Given
var console = new TestConsole().Width(70);
var text = new FigletText(FigletFont.Default, "Patrik was here");
// When
console.Write(text);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Wrapped")]
public async Task Should_Render_Wrapped_Text_Correctly()
{
// Given
var console = new TestConsole().Width(70);
var text = new FigletText(FigletFont.Default, "Spectre.Console");
// When
console.Write(text);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_LeftAligned")]
public async Task Should_Render_Left_Aligned_Text_Correctly()
{
// Given
var console = new TestConsole().Width(120);
var text = new FigletText(FigletFont.Default, "Spectre.Console")
.Alignment(Justify.Left);
// When
console.Write(text);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Centered")]
public async Task Should_Render_Centered_Text_Correctly()
{
// Given
var console = new TestConsole().Width(120);
var text = new FigletText(FigletFont.Default, "Spectre.Console")
.Alignment(Justify.Center);
// When
console.Write(text);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_RightAligned")]
public async Task Should_Render_Right_Aligned_Text_Correctly()
{
// Given
var console = new TestConsole().Width(120);
var text = new FigletText(FigletFont.Default, "Spectre.Console")
.Alignment(Justify.Right);
// When
console.Write(text);
// Then
await Verifier.Verify(console.Output);
}
}

View File

@ -1,200 +1,91 @@
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("Widgets/Grid")]
public sealed class GridTests
{
[UsesVerify]
[ExpectationPath("Widgets/Grid")]
public sealed class GridTests
public sealed class TheAddColumnMethod
{
public sealed class TheAddColumnMethod
[Fact]
public void Should_Throw_If_Rows_Are_Not_Empty()
{
[Fact]
public void Should_Throw_If_Rows_Are_Not_Empty()
{
// Given
var grid = new Grid();
grid.AddColumn();
grid.AddRow("Hello World!");
// Given
var grid = new Grid();
grid.AddColumn();
grid.AddRow("Hello World!");
// When
var result = Record.Exception(() => grid.AddColumn());
// When
var result = Record.Exception(() => grid.AddColumn());
// Then
result.ShouldBeOfType<InvalidOperationException>()
.Message.ShouldBe("Cannot add new columns to grid with existing rows.");
}
// Then
result.ShouldBeOfType<InvalidOperationException>()
.Message.ShouldBe("Cannot add new columns to grid with existing rows.");
}
}
public sealed class TheAddRowMethod
{
[Fact]
public void Should_Throw_If_Rows_Are_Null()
{
// Given
var grid = new Grid();
// When
var result = Record.Exception(() => grid.AddRow(null));
// Then
result.ShouldBeOfType<ArgumentNullException>()
.ParamName.ShouldBe("columns");
}
public sealed class TheAddRowMethod
[Fact]
public void Should_Add_Empty_Items_If_User_Provides_Less_Row_Items_Than_Columns()
{
[Fact]
public void Should_Throw_If_Rows_Are_Null()
{
// Given
var grid = new Grid();
// Given
var grid = new Grid();
grid.AddColumn();
grid.AddColumn();
// When
var result = Record.Exception(() => grid.AddRow(null));
// When
grid.AddRow("Foo");
// Then
result.ShouldBeOfType<ArgumentNullException>()
.ParamName.ShouldBe("columns");
}
[Fact]
public void Should_Add_Empty_Items_If_User_Provides_Less_Row_Items_Than_Columns()
{
// Given
var grid = new Grid();
grid.AddColumn();
grid.AddColumn();
// When
grid.AddRow("Foo");
// Then
grid.Rows.Count.ShouldBe(1);
}
[Fact]
public void Should_Throw_If_Row_Columns_Are_Greater_Than_Number_Of_Columns()
{
// Given
var grid = new Grid();
grid.AddColumn();
// When
var result = Record.Exception(() => grid.AddRow("Foo", "Bar"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("The number of row columns are greater than the number of grid columns.");
}
// Then
grid.Rows.Count.ShouldBe(1);
}
[UsesVerify]
[ExpectationPath("AddEmptyRow")]
public sealed class TheAddEmptyRowMethod
[Fact]
public void Should_Throw_If_Row_Columns_Are_Greater_Than_Number_Of_Columns()
{
[Fact]
[Expectation("Render")]
public Task Should_Add_Empty_Row()
{
// Given
var console = new TestConsole();
var grid = new Grid();
grid.AddColumns(2);
grid.AddRow("Foo", "Bar");
grid.AddEmptyRow();
grid.AddRow("Qux", "Corgi");
grid.AddEmptyRow();
// Given
var grid = new Grid();
grid.AddColumn();
// When
console.Write(grid);
// When
var result = Record.Exception(() => grid.AddRow("Foo", "Bar"));
// Then
return Verifier.Verify(console.Output);
}
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("The number of row columns are greater than the number of grid columns.");
}
}
[UsesVerify]
[ExpectationPath("AddEmptyRow")]
public sealed class TheAddEmptyRowMethod
{
[Fact]
[Expectation("Render")]
public Task Should_Render_Grid_Correctly()
public Task Should_Add_Empty_Row()
{
// Given
var console = new TestConsole();
var grid = new Grid();
grid.AddColumn();
grid.AddColumn();
grid.AddColumn();
grid.AddRow("Qux", "Corgi", "Waldo");
grid.AddRow("Grault", "Garply", "Fred");
// When
console.Write(grid);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_2")]
public Task Should_Render_Grid_Correctly_2()
{
var console = new TestConsole();
var grid = new Grid();
grid.AddColumn(new GridColumn { NoWrap = true });
grid.AddColumn(new GridColumn { Padding = new Padding(2, 0, 0, 0) });
grid.AddRow("[bold]Options[/]", string.Empty);
grid.AddRow(" [blue]-h[/], [blue]--help[/]", "Show command line help.");
grid.AddRow(" [blue]-c[/], [blue]--configuration[/]", "The configuration to run for.\nThe default for most projects is [green]Debug[/].");
// When
console.Write(grid);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Alignment")]
public Task Should_Render_Grid_Column_Alignment_Correctly()
{
// Given
var console = new TestConsole();
var grid = new Grid();
grid.AddColumn(new GridColumn { Alignment = Justify.Right });
grid.AddColumn(new GridColumn { Alignment = Justify.Center });
grid.AddColumn(new GridColumn { Alignment = Justify.Left });
grid.AddRow("Foo", "Bar", "Baz");
grid.AddRow("Qux", "Corgi", "Waldo");
grid.AddRow("Grault", "Garply", "Fred");
// When
console.Write(grid);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Padding")]
public Task Should_Use_Default_Padding()
{
// Given
var console = new TestConsole();
var grid = new Grid();
grid.AddColumns(3);
grid.AddRow("Foo", "Bar", "Baz");
grid.AddRow("Qux", "Corgi", "Waldo");
grid.AddRow("Grault", "Garply", "Fred");
// When
console.Write(grid);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_ExplicitPadding")]
public Task Should_Render_Explicit_Grid_Column_Padding_Correctly()
{
// Given
var console = new TestConsole();
var grid = new Grid();
grid.AddColumn(new GridColumn { Padding = new Padding(3, 0, 0, 0) });
grid.AddColumn(new GridColumn { Padding = new Padding(0, 0, 0, 0) });
grid.AddColumn(new GridColumn { Padding = new Padding(0, 0, 3, 0) });
grid.AddRow("Foo", "Bar", "Baz");
grid.AddRow("Qux", "Corgi", "Waldo");
grid.AddRow("Grault", "Garply", "Fred");
grid.AddColumns(2);
grid.AddRow("Foo", "Bar");
grid.AddEmptyRow();
grid.AddRow("Qux", "Corgi");
grid.AddEmptyRow();
// When
console.Write(grid);
@ -203,4 +94,104 @@ namespace Spectre.Console.Tests.Unit
return Verifier.Verify(console.Output);
}
}
}
[Fact]
[Expectation("Render")]
public Task Should_Render_Grid_Correctly()
{
// Given
var console = new TestConsole();
var grid = new Grid();
grid.AddColumn();
grid.AddColumn();
grid.AddColumn();
grid.AddRow("Qux", "Corgi", "Waldo");
grid.AddRow("Grault", "Garply", "Fred");
// When
console.Write(grid);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_2")]
public Task Should_Render_Grid_Correctly_2()
{
var console = new TestConsole();
var grid = new Grid();
grid.AddColumn(new GridColumn { NoWrap = true });
grid.AddColumn(new GridColumn { Padding = new Padding(2, 0, 0, 0) });
grid.AddRow("[bold]Options[/]", string.Empty);
grid.AddRow(" [blue]-h[/], [blue]--help[/]", "Show command line help.");
grid.AddRow(" [blue]-c[/], [blue]--configuration[/]", "The configuration to run for.\nThe default for most projects is [green]Debug[/].");
// When
console.Write(grid);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Alignment")]
public Task Should_Render_Grid_Column_Alignment_Correctly()
{
// Given
var console = new TestConsole();
var grid = new Grid();
grid.AddColumn(new GridColumn { Alignment = Justify.Right });
grid.AddColumn(new GridColumn { Alignment = Justify.Center });
grid.AddColumn(new GridColumn { Alignment = Justify.Left });
grid.AddRow("Foo", "Bar", "Baz");
grid.AddRow("Qux", "Corgi", "Waldo");
grid.AddRow("Grault", "Garply", "Fred");
// When
console.Write(grid);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Padding")]
public Task Should_Use_Default_Padding()
{
// Given
var console = new TestConsole();
var grid = new Grid();
grid.AddColumns(3);
grid.AddRow("Foo", "Bar", "Baz");
grid.AddRow("Qux", "Corgi", "Waldo");
grid.AddRow("Grault", "Garply", "Fred");
// When
console.Write(grid);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_ExplicitPadding")]
public Task Should_Render_Explicit_Grid_Column_Padding_Correctly()
{
// Given
var console = new TestConsole();
var grid = new Grid();
grid.AddColumn(new GridColumn { Padding = new Padding(3, 0, 0, 0) });
grid.AddColumn(new GridColumn { Padding = new Padding(0, 0, 0, 0) });
grid.AddColumn(new GridColumn { Padding = new Padding(0, 0, 3, 0) });
grid.AddRow("Foo", "Bar", "Baz");
grid.AddRow("Qux", "Corgi", "Waldo");
grid.AddRow("Grault", "Garply", "Fred");
// When
console.Write(grid);
// Then
return Verifier.Verify(console.Output);
}
}

View File

@ -1,143 +1,137 @@
using System;
using Shouldly;
using Spectre.Console.Testing;
using Xunit;
namespace Spectre.Console.Tests.Unit;
namespace Spectre.Console.Tests.Unit
public sealed class MarkupTests
{
public sealed class MarkupTests
public sealed class TheLengthProperty
{
public sealed class TheLengthProperty
{
[Theory]
[InlineData("Hello", 5)]
[InlineData("Hello\nWorld", 11)]
[InlineData("[yellow]Hello[/]", 5)]
public void Should_Return_The_Number_Of_Characters(string input, int expected)
{
// Given
var markup = new Markup(input);
// When
var result = markup.Length;
// Then
result.ShouldBe(expected);
}
}
public sealed class TheLinesProperty
{
[Theory]
[InlineData("Hello", 1)]
[InlineData("Hello\nWorld", 2)]
[InlineData("[yellow]Hello[/]\nWorld", 2)]
public void Should_Return_The_Number_Of_Lines(string input, int expected)
{
// Given
var markup = new Markup(input);
// When
var result = markup.Lines;
// Then
result.ShouldBe(expected);
}
}
public sealed class TheEscapeMethod
{
[Theory]
[InlineData("Hello World", "Hello World")]
[InlineData("Hello World [", "Hello World [[")]
[InlineData("Hello World ]", "Hello World ]]")]
[InlineData("Hello [World]", "Hello [[World]]")]
[InlineData("Hello [[World]]", "Hello [[[[World]]]]")]
public void Should_Escape_Markup_As_Expected(string input, string expected)
{
// Given, When
var result = Markup.Escape(input);
// Then
result.ShouldBe(expected);
}
}
public sealed class TheRemoveMethod
{
[Theory]
[InlineData("Hello World", "Hello World")]
[InlineData("Hello [blue]World", "Hello World")]
[InlineData("Hello [blue]World[/]", "Hello World")]
public void Should_Remove_Markup_From_Text(string input, string expected)
{
// Given, When
var result = Markup.Remove(input);
// Then
result.ShouldBe(expected);
}
}
[Theory]
[InlineData("Hello [[ World ]")]
[InlineData("Hello [[ World ] !")]
public void Should_Throw_If_Closing_Tag_Is_Not_Properly_Escaped(string input)
[InlineData("Hello", 5)]
[InlineData("Hello\nWorld", 11)]
[InlineData("[yellow]Hello[/]", 5)]
public void Should_Return_The_Number_Of_Characters(string input, int expected)
{
// Given
var console = new TestConsole();
// When
var result = Record.Exception(() => new Markup(input));
// Then
result.ShouldNotBeNull();
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("Encountered unescaped ']' token at position 16");
}
[Fact]
public void Should_Escape_Markup_Blocks_As_Expected()
{
// Given
var console = new TestConsole();
var markup = new Markup("Hello [[ World ]] !");
// When
console.Write(markup);
// Then
console.Output.ShouldBe("Hello [ World ] !");
}
[Theory]
[InlineData("Hello [link=http://example.com]example.com[/]", "Hello example.com")]
[InlineData("Hello [link=http://example.com]http://example.com[/]", "Hello http://example.com")]
public void Should_Render_Links_As_Expected(string input, string output)
{
// Given
var console = new TestConsole();
var markup = new Markup(input);
// When
console.Write(markup);
var result = markup.Length;
// Then
console.Output.ShouldBe(output);
}
[Fact]
public void Should_Not_Fail_With_Brackets_On_Calls_Without_Args()
{
// Given
var console = new TestConsole();
// When
console.MarkupLine("{");
// Then
console.Output.NormalizeLineEndings()
.ShouldBe("{\n");
result.ShouldBe(expected);
}
}
}
public sealed class TheLinesProperty
{
[Theory]
[InlineData("Hello", 1)]
[InlineData("Hello\nWorld", 2)]
[InlineData("[yellow]Hello[/]\nWorld", 2)]
public void Should_Return_The_Number_Of_Lines(string input, int expected)
{
// Given
var markup = new Markup(input);
// When
var result = markup.Lines;
// Then
result.ShouldBe(expected);
}
}
public sealed class TheEscapeMethod
{
[Theory]
[InlineData("Hello World", "Hello World")]
[InlineData("Hello World [", "Hello World [[")]
[InlineData("Hello World ]", "Hello World ]]")]
[InlineData("Hello [World]", "Hello [[World]]")]
[InlineData("Hello [[World]]", "Hello [[[[World]]]]")]
public void Should_Escape_Markup_As_Expected(string input, string expected)
{
// Given, When
var result = Markup.Escape(input);
// Then
result.ShouldBe(expected);
}
}
public sealed class TheRemoveMethod
{
[Theory]
[InlineData("Hello World", "Hello World")]
[InlineData("Hello [blue]World", "Hello World")]
[InlineData("Hello [blue]World[/]", "Hello World")]
public void Should_Remove_Markup_From_Text(string input, string expected)
{
// Given, When
var result = Markup.Remove(input);
// Then
result.ShouldBe(expected);
}
}
[Theory]
[InlineData("Hello [[ World ]")]
[InlineData("Hello [[ World ] !")]
public void Should_Throw_If_Closing_Tag_Is_Not_Properly_Escaped(string input)
{
// Given
var console = new TestConsole();
// When
var result = Record.Exception(() => new Markup(input));
// Then
result.ShouldNotBeNull();
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("Encountered unescaped ']' token at position 16");
}
[Fact]
public void Should_Escape_Markup_Blocks_As_Expected()
{
// Given
var console = new TestConsole();
var markup = new Markup("Hello [[ World ]] !");
// When
console.Write(markup);
// Then
console.Output.ShouldBe("Hello [ World ] !");
}
[Theory]
[InlineData("Hello [link=http://example.com]example.com[/]", "Hello example.com")]
[InlineData("Hello [link=http://example.com]http://example.com[/]", "Hello http://example.com")]
public void Should_Render_Links_As_Expected(string input, string output)
{
// Given
var console = new TestConsole();
var markup = new Markup(input);
// When
console.Write(markup);
// Then
console.Output.ShouldBe(output);
}
[Fact]
public void Should_Not_Fail_With_Brackets_On_Calls_Without_Args()
{
// Given
var console = new TestConsole();
// When
console.MarkupLine("{");
// Then
console.Output.NormalizeLineEndings()
.ShouldBe("{\n");
}
}

View File

@ -1,75 +1,68 @@
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/Padder")]
public sealed class PadderTests
{
[UsesVerify]
[ExpectationPath("Widgets/Padder")]
public sealed class PadderTests
[Fact]
[Expectation("Render")]
public Task Should_Render_Padded_Object_Correctly()
{
[Fact]
[Expectation("Render")]
public Task Should_Render_Padded_Object_Correctly()
{
// Given
var console = new TestConsole().Width(60);
var table = new Table();
table.AddColumn("Foo");
table.AddColumn("Bar");
table.AddRow("Baz", "Qux");
table.AddRow("Corgi", "Waldo");
// Given
var console = new TestConsole().Width(60);
var table = new Table();
table.AddColumn("Foo");
table.AddColumn("Bar");
table.AddRow("Baz", "Qux");
table.AddRow("Corgi", "Waldo");
// When
console.Write(new Padder(table).Padding(1, 2, 3, 4));
// When
console.Write(new Padder(table).Padding(1, 2, 3, 4));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Expanded")]
public Task Should_Render_Expanded_Padded_Object_Correctly()
{
// Given
var console = new TestConsole().Width(60);
var table = new Table();
table.AddColumn("Foo");
table.AddColumn("Bar");
table.AddRow("Baz", "Qux");
table.AddRow("Corgi", "Waldo");
// When
console.Write(new Padder(table)
.Padding(1, 2, 3, 4)
.Expand());
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Nested")]
public Task Should_Render_Padded_Object_Correctly_When_Nested_Within_Other_Object()
{
// Given
var console = new TestConsole().Width(60);
var table = new Table();
table.AddColumn("Foo");
table.AddColumn("Bar", c => c.PadLeft(0).PadRight(0));
table.AddRow("Baz", "Qux");
table.AddRow(new Text("Corgi"), new Padder(new Panel("Waldo"))
.Padding(2, 1));
// When
console.Write(new Padder(table)
.Padding(1, 2, 3, 4)
.Expand());
// Then
return Verifier.Verify(console.Output);
}
// Then
return Verifier.Verify(console.Output);
}
}
[Fact]
[Expectation("Render_Expanded")]
public Task Should_Render_Expanded_Padded_Object_Correctly()
{
// Given
var console = new TestConsole().Width(60);
var table = new Table();
table.AddColumn("Foo");
table.AddColumn("Bar");
table.AddRow("Baz", "Qux");
table.AddRow("Corgi", "Waldo");
// When
console.Write(new Padder(table)
.Padding(1, 2, 3, 4)
.Expand());
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Nested")]
public Task Should_Render_Padded_Object_Correctly_When_Nested_Within_Other_Object()
{
// Given
var console = new TestConsole().Width(60);
var table = new Table();
table.AddColumn("Foo");
table.AddColumn("Bar", c => c.PadLeft(0).PadRight(0));
table.AddRow("Baz", "Qux");
table.AddRow(new Text("Corgi"), new Padder(new Panel("Waldo"))
.Padding(2, 1));
// When
console.Write(new Padder(table)
.Padding(1, 2, 3, 4)
.Expand());
// Then
return Verifier.Verify(console.Output);
}
}

View File

@ -1,309 +1,300 @@
using System.Collections.Generic;
using System.Threading.Tasks;
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("Widgets/Panel")]
public sealed class PanelTests
{
[UsesVerify]
[ExpectationPath("Widgets/Panel")]
public sealed class PanelTests
[Fact]
[Expectation("Render")]
public Task Should_Render_Panel()
{
[Fact]
[Expectation("Render")]
public Task Should_Render_Panel()
{
// Given
var console = new TestConsole();
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Text("Hello World")));
// When
console.Write(new Panel(new Text("Hello World")));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_ZeroPadding")]
public Task Should_Render_Panel_With_Padding_Set_To_Zero()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Text("Hello World"))
{
Padding = new Padding(0, 0, 0, 0),
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Padding")]
public Task Should_Render_Panel_With_Padding()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Text("Hello World"))
{
Padding = new Padding(3, 1, 5, 2),
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header")]
public Task Should_Render_Panel_With_Header()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel("Hello World")
{
Header = new PanelHeader("Greeting"),
Expand = true,
Padding = new Padding(2, 0, 2, 0),
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_LeftAligned")]
public Task Should_Render_Panel_With_Left_Aligned_Header()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel("Hello World")
{
Header = new PanelHeader("Greeting").LeftAligned(),
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_Centered")]
public Task Should_Render_Panel_With_Centered_Header()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel("Hello World")
{
Header = new PanelHeader("Greeting").Centered(),
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_RightAligned")]
public Task Should_Render_Panel_With_Right_Aligned_Header()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel("Hello World")
{
Header = new PanelHeader("Greeting").RightAligned(),
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_Collapse")]
public Task Should_Collapse_Header_If_It_Will_Not_Fit()
{
// Given
var console = new TestConsole().Width(10);
// When
console.Write(new Panel("Hello World")
{
Header = new PanelHeader("Greeting"),
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Unicode")]
public Task Should_Render_Panel_With_Unicode_Correctly()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Text(" \n💩\n ")));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Multiline")]
public Task Should_Render_Panel_With_Multiple_Lines()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Text("Hello World\nFoo Bar")));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_LineEndings")]
public Task Should_Preserve_Explicit_Line_Ending()
{
// Given
var console = new TestConsole();
var text = new Panel(
new Markup("I heard [underline on blue]you[/] like 📦\n\n\n\nSo I put a 📦 in a 📦"));
// When
console.Write(text);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Expand")]
public Task Should_Expand_Panel_If_Enabled()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Text("Hello World"))
{
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Child_RightAligned")]
public Task Should_Justify_Child_To_Right_Correctly()
{
// Given
var console = new TestConsole().Width(25);
// When
console.Write(
new Panel(new Text("Hello World").RightAligned())
{
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Child_Centered")]
public Task Should_Center_Child_Correctly()
{
// Given
var console = new TestConsole().Width(25);
// When
console.Write(
new Panel(new Text("Hello World").Centered())
{
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Child_Panel")]
public Task Should_Render_Panel_Inside_Panel_Correctly()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Panel(new Text("Hello World"))));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Wrap")]
public Task Should_Wrap_Content_Correctly()
{
// Given
var console = new TestConsole().Width(84);
var rows = new List<IRenderable>();
var grid = new Grid();
grid.AddColumn(new GridColumn().PadLeft(2).PadRight(0));
grid.AddColumn(new GridColumn().PadLeft(1).PadRight(0));
grid.AddRow("at", "[grey]System.Runtime.CompilerServices.TaskAwaiter.[/][yellow]HandleNonSuccessAndDebuggerNotification[/]([blue]Task[/] task)");
rows.Add(grid);
var panel = new Panel(grid)
.Expand().RoundedBorder()
.BorderStyle(new Style().Foreground(Color.Grey))
.Header("[grey]Short paths[/]");
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_CJK")]
public Task Should_Wrap_Table_With_CJK_Tables_In_Panel_Correctly()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("测试");
table.AddRow("测试");
var panel = new Panel(table);
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
// Then
return Verifier.Verify(console.Output);
}
}
[Fact]
[Expectation("Render_ZeroPadding")]
public Task Should_Render_Panel_With_Padding_Set_To_Zero()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Text("Hello World"))
{
Padding = new Padding(0, 0, 0, 0),
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Padding")]
public Task Should_Render_Panel_With_Padding()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Text("Hello World"))
{
Padding = new Padding(3, 1, 5, 2),
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header")]
public Task Should_Render_Panel_With_Header()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel("Hello World")
{
Header = new PanelHeader("Greeting"),
Expand = true,
Padding = new Padding(2, 0, 2, 0),
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_LeftAligned")]
public Task Should_Render_Panel_With_Left_Aligned_Header()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel("Hello World")
{
Header = new PanelHeader("Greeting").LeftAligned(),
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_Centered")]
public Task Should_Render_Panel_With_Centered_Header()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel("Hello World")
{
Header = new PanelHeader("Greeting").Centered(),
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_RightAligned")]
public Task Should_Render_Panel_With_Right_Aligned_Header()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel("Hello World")
{
Header = new PanelHeader("Greeting").RightAligned(),
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_Collapse")]
public Task Should_Collapse_Header_If_It_Will_Not_Fit()
{
// Given
var console = new TestConsole().Width(10);
// When
console.Write(new Panel("Hello World")
{
Header = new PanelHeader("Greeting"),
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Unicode")]
public Task Should_Render_Panel_With_Unicode_Correctly()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Text(" \n💩\n ")));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Multiline")]
public Task Should_Render_Panel_With_Multiple_Lines()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Text("Hello World\nFoo Bar")));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_LineEndings")]
public Task Should_Preserve_Explicit_Line_Ending()
{
// Given
var console = new TestConsole();
var text = new Panel(
new Markup("I heard [underline on blue]you[/] like 📦\n\n\n\nSo I put a 📦 in a 📦"));
// When
console.Write(text);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Expand")]
public Task Should_Expand_Panel_If_Enabled()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Text("Hello World"))
{
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Child_RightAligned")]
public Task Should_Justify_Child_To_Right_Correctly()
{
// Given
var console = new TestConsole().Width(25);
// When
console.Write(
new Panel(new Text("Hello World").RightAligned())
{
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Child_Centered")]
public Task Should_Center_Child_Correctly()
{
// Given
var console = new TestConsole().Width(25);
// When
console.Write(
new Panel(new Text("Hello World").Centered())
{
Expand = true,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Child_Panel")]
public Task Should_Render_Panel_Inside_Panel_Correctly()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Panel(new Text("Hello World"))));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Wrap")]
public Task Should_Wrap_Content_Correctly()
{
// Given
var console = new TestConsole().Width(84);
var rows = new List<IRenderable>();
var grid = new Grid();
grid.AddColumn(new GridColumn().PadLeft(2).PadRight(0));
grid.AddColumn(new GridColumn().PadLeft(1).PadRight(0));
grid.AddRow("at", "[grey]System.Runtime.CompilerServices.TaskAwaiter.[/][yellow]HandleNonSuccessAndDebuggerNotification[/]([blue]Task[/] task)");
rows.Add(grid);
var panel = new Panel(grid)
.Expand().RoundedBorder()
.BorderStyle(new Style().Foreground(Color.Grey))
.Header("[grey]Short paths[/]");
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_CJK")]
public Task Should_Wrap_Table_With_CJK_Tables_In_Panel_Correctly()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("测试");
table.AddRow("测试");
var panel = new Panel(table);
// When
console.Write(panel);
// Then
return Verifier.Verify(console.Output);
}
}

View File

@ -1,83 +1,75 @@
using System.Threading.Tasks;
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("Widgets/Rows")]
public sealed class RowsTests
{
[UsesVerify]
[ExpectationPath("Widgets/Rows")]
public sealed class RowsTests
[Fact]
[Expectation("Render")]
public Task Should_Render_Rows()
{
[Fact]
[Expectation("Render")]
public Task Should_Render_Rows()
{
// Given
var console = new TestConsole().Width(60);
var rows = new Rows(
new IRenderable[]
{
// Given
var console = new TestConsole().Width(60);
var rows = new Rows(
new IRenderable[]
{
new Markup("Hello"),
new Table()
.AddColumns("Foo", "Bar")
.AddRow("Baz", "Qux"),
new Markup("World"),
});
});
// When
console.Write(rows);
// When
console.Write(rows);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Nested")]
public Task Should_Render_Rows_Correctly_Inside_Other_Widget()
{
// Given
var console = new TestConsole().Width(60);
var table = new Table()
.AddColumns("Foo", "Bar")
.AddRow("HELLO WORLD")
.AddRow(
new Rows(new IRenderable[]
{
new Markup("Hello"),
new Markup("World"),
}), new Text("Qux"));
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Expanded_And_Nested")]
public Task Should_Render_Rows_Correctly_Inside_Other_Widget_When_Expanded()
{
// Given
var console = new TestConsole().Width(60);
var table = new Table()
.AddColumns("Foo", "Bar")
.AddRow("HELLO WORLD")
.AddRow(
new Rows(new IRenderable[]
{
new Markup("Hello"),
new Markup("World"),
}).Expand(), new Text("Qux"));
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
// Then
return Verifier.Verify(console.Output);
}
}
[Fact]
[Expectation("Render_Nested")]
public Task Should_Render_Rows_Correctly_Inside_Other_Widget()
{
// Given
var console = new TestConsole().Width(60);
var table = new Table()
.AddColumns("Foo", "Bar")
.AddRow("HELLO WORLD")
.AddRow(
new Rows(new IRenderable[]
{
new Markup("Hello"),
new Markup("World"),
}), new Text("Qux"));
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Expanded_And_Nested")]
public Task Should_Render_Rows_Correctly_Inside_Other_Widget_When_Expanded()
{
// Given
var console = new TestConsole().Width(60);
var table = new Table()
.AddColumns("Foo", "Bar")
.AddRow("HELLO WORLD")
.AddRow(
new Rows(new IRenderable[]
{
new Markup("Hello"),
new Markup("World"),
}).Expand(), new Text("Qux"));
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
}

View File

@ -1,158 +1,150 @@
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("Widgets/Rule")]
public sealed class RuleTests
{
[UsesVerify]
[ExpectationPath("Widgets/Rule")]
public sealed class RuleTests
[Fact]
[Expectation("Render")]
public Task Should_Render_Default_Rule_Without_Title()
{
[Fact]
[Expectation("Render")]
public Task Should_Render_Default_Rule_Without_Title()
{
// Given
var console = new TestConsole().Width(40);
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule());
// When
console.Write(new Rule());
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Border_NoHeader")]
public Task Should_Render_Default_Rule_With_Specified_Border()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule().DoubleBorder());
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Border_Header")]
public Task Should_Render_With_Specified_Box()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule("Hello World").DoubleBorder());
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_DefaultAlignment")]
public Task Should_Render_Default_Rule_With_Title_Centered_By_Default()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule("Hello World"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_LeftAligned")]
public Task Should_Render_Default_Rule_With_Title_Left_Aligned()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule("Hello World")
{
Alignment = Justify.Left,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_RightAligned")]
public Task Should_Render_Default_Rule_With_Title_Right_Aligned()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule("Hello World")
{
Alignment = Justify.Right,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Linebreaks")]
public Task Should_Convert_Line_Breaks_In_Title_To_Spaces()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule("Hello\nWorld\r\n!"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Truncate")]
public Task Should_Truncate_Title()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule(" Hello World "));
// Then
return Verifier.Verify(console.Output);
}
[Theory]
[InlineData(1, "Hello World Hello World Hello World Hello World Hello World", "─")]
[InlineData(2, "Hello World Hello World Hello World Hello World Hello World", "──")]
[InlineData(3, "Hello World Hello World Hello World Hello World Hello World", "───")]
[InlineData(4, "Hello World Hello World Hello World Hello World Hello World", "────")]
[InlineData(5, "Hello World Hello World Hello World Hello World Hello World", "─────")]
[InlineData(6, "Hello World Hello World Hello World Hello World Hello World", "──────")]
[InlineData(7, "Hello World Hello World Hello World Hello World Hello World", "───────")]
[InlineData(8, "Hello World Hello World Hello World Hello World Hello World", "── H… ──")]
[InlineData(8, "A", "── A ───")]
[InlineData(8, "AB", "── AB ──")]
[InlineData(8, "ABC", "── A… ──")]
[InlineData(40, "Hello World Hello World Hello World Hello World Hello World", "──── Hello World Hello World Hello… ────")]
public void Should_Truncate_Too_Long_Title(int width, string input, string expected)
{
// Given
var console = new TestConsole().Width(width);
// When
console.Write(new Rule(input));
// Then
console.Lines.Count.ShouldBe(1);
console.Lines[0].ShouldBe(expected);
}
// Then
return Verifier.Verify(console.Output);
}
}
[Fact]
[Expectation("Render_Border_NoHeader")]
public Task Should_Render_Default_Rule_With_Specified_Border()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule().DoubleBorder());
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Border_Header")]
public Task Should_Render_With_Specified_Box()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule("Hello World").DoubleBorder());
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_DefaultAlignment")]
public Task Should_Render_Default_Rule_With_Title_Centered_By_Default()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule("Hello World"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_LeftAligned")]
public Task Should_Render_Default_Rule_With_Title_Left_Aligned()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule("Hello World")
{
Alignment = Justify.Left,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Header_RightAligned")]
public Task Should_Render_Default_Rule_With_Title_Right_Aligned()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule("Hello World")
{
Alignment = Justify.Right,
});
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Linebreaks")]
public Task Should_Convert_Line_Breaks_In_Title_To_Spaces()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule("Hello\nWorld\r\n!"));
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_Truncate")]
public Task Should_Truncate_Title()
{
// Given
var console = new TestConsole().Width(40);
// When
console.Write(new Rule(" Hello World "));
// Then
return Verifier.Verify(console.Output);
}
[Theory]
[InlineData(1, "Hello World Hello World Hello World Hello World Hello World", "─")]
[InlineData(2, "Hello World Hello World Hello World Hello World Hello World", "──")]
[InlineData(3, "Hello World Hello World Hello World Hello World Hello World", "───")]
[InlineData(4, "Hello World Hello World Hello World Hello World Hello World", "────")]
[InlineData(5, "Hello World Hello World Hello World Hello World Hello World", "─────")]
[InlineData(6, "Hello World Hello World Hello World Hello World Hello World", "──────")]
[InlineData(7, "Hello World Hello World Hello World Hello World Hello World", "───────")]
[InlineData(8, "Hello World Hello World Hello World Hello World Hello World", "── H… ──")]
[InlineData(8, "A", "── A ───")]
[InlineData(8, "AB", "── AB ──")]
[InlineData(8, "ABC", "── A… ──")]
[InlineData(40, "Hello World Hello World Hello World Hello World Hello World", "──── Hello World Hello World Hello… ────")]
public void Should_Truncate_Too_Long_Title(int width, string input, string expected)
{
// Given
var console = new TestConsole().Width(width);
// When
console.Write(new Rule(input));
// Then
console.Lines.Count.ShouldBe(1);
console.Lines[0].ShouldBe(expected);
}
}

View File

@ -1,131 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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/Table/Rows/Extensions")]
public sealed class TableRowCollectionExtensionsTests
{
[UsesVerify]
[ExpectationPath("Widgets/Table/Rows/Extensions")]
public sealed class TableRowCollectionExtensionsTests
public sealed class TheAddRowMethod
{
[UsesVerify]
public sealed class TheAddRowMethod
[Fact]
[Expectation("Add", "Renderables")]
public Task Should_Add_Renderables()
{
[Fact]
[Expectation("Add", "Renderables")]
public Task Should_Add_Renderables()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddRow(new[] { new Text("1"), new Text("1-2") });
table.AddRow(new[] { new Text("2"), new Text("2-2") });
table.AddRow(new[] { new Text("3"), new Text("3-2") });
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddRow(new[] { new Text("1"), new Text("1-2") });
table.AddRow(new[] { new Text("2"), new Text("2-2") });
table.AddRow(new[] { new Text("3"), new Text("3-2") });
// When
console.Write(table);
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Add", "Strings")]
public Task Should_Add_Strings()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddRow("1", "1-2");
table.AddRow("2", "2-2");
table.AddRow("3", "3-2");
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
// Then
return Verifier.Verify(console.Output);
}
[UsesVerify]
public sealed class TheInsertRowMethod
[Fact]
[Expectation("Add", "Strings")]
public Task Should_Add_Strings()
{
[Fact]
[Expectation("Insert", "Renderables")]
public Task Should_Insert_Renderables()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddRow(new[] { new Text("1"), new Text("1-2") });
table.AddRow(new[] { new Text("2"), new Text("2-2") });
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddRow("1", "1-2");
table.AddRow("2", "2-2");
table.AddRow("3", "3-2");
// When
table.InsertRow(1, new[] { new Text("3"), new Text("3-2") });
// When
console.Write(table);
// Then
console.Write(table);
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Insert", "Strings")]
public Task Should_Insert_Strings()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddRow("1", "1-2");
table.AddRow("2", "2-2");
// When
table.InsertRow(1, "3", "3-2");
// Then
console.Write(table);
return Verifier.Verify(console.Output);
}
}
[UsesVerify]
public sealed class TheRemoveRowMethod
{
[Fact]
[Expectation("Remove")]
public Task Should_Remove_Row()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddRow(new[] { new Text("1"), new Text("1-2") });
table.AddRow(new[] { new Text("2"), new Text("2-2") });
table.AddRow(new[] { new Text("3"), new Text("3-2") });
// When
table.RemoveRow(1);
// Then
console.Write(table);
return Verifier.Verify(console.Output);
}
// Then
return Verifier.Verify(console.Output);
}
}
}
[UsesVerify]
public sealed class TheInsertRowMethod
{
[Fact]
[Expectation("Insert", "Renderables")]
public Task Should_Insert_Renderables()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddRow(new[] { new Text("1"), new Text("1-2") });
table.AddRow(new[] { new Text("2"), new Text("2-2") });
// When
table.InsertRow(1, new[] { new Text("3"), new Text("3-2") });
// Then
console.Write(table);
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Insert", "Strings")]
public Task Should_Insert_Strings()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddRow("1", "1-2");
table.AddRow("2", "2-2");
// When
table.InsertRow(1, "3", "3-2");
// Then
console.Write(table);
return Verifier.Verify(console.Output);
}
}
[UsesVerify]
public sealed class TheRemoveRowMethod
{
[Fact]
[Expectation("Remove")]
public Task Should_Remove_Row()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddRow(new[] { new Text("1"), new Text("1-2") });
table.AddRow(new[] { new Text("2"), new Text("2-2") });
table.AddRow(new[] { new Text("3"), new Text("3-2") });
// When
table.RemoveRow(1);
// Then
console.Write(table);
return Verifier.Verify(console.Output);
}
}
}

View File

@ -1,366 +1,353 @@
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
[ExpectationPath("Widgets/Table/Rows")]
public sealed class TableRowCollectionTests
{
[ExpectationPath("Widgets/Table/Rows")]
public sealed class TableRowCollectionTests
[UsesVerify]
public sealed class TheAddMethod
{
[UsesVerify]
public sealed class TheAddMethod
[Fact]
public void Should_Throw_If_Columns_Are_Null()
{
[Fact]
public void Should_Throw_If_Columns_Are_Null()
{
// Given
var table = new Table();
// Given
var table = new Table();
// When
var result = Record.Exception(() => table.Rows.Add(null));
// When
var result = Record.Exception(() => table.Rows.Add(null));
// Then
result.ShouldBeOfType<ArgumentNullException>()
.ParamName.ShouldBe("columns");
}
[Fact]
public void Should_Add_Row_To_Collection()
{
// Given
var table = new Table();
table.AddColumn("Column #1");
// When
table.Rows.Add(new[] { Text.Empty });
// Then
table.Rows.Count.ShouldBe(1);
}
[Fact]
public void Should_Return_Index_Of_Added_Row()
{
// Given
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { Text.Empty });
// When
var result = table.Rows.Add(new[] { Text.Empty });
// Then
result.ShouldBe(1);
}
[Fact]
[Expectation("Add")]
public Task Should_Add_Item_At_Correct_Place()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3") });
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
// Then
result.ShouldBeOfType<ArgumentNullException>()
.ParamName.ShouldBe("columns");
}
[UsesVerify]
public sealed class TheInsertMethod
[Fact]
public void Should_Add_Row_To_Collection()
{
[Fact]
public void Should_Throw_If_Columns_Are_Null()
{
// Given
var table = new Table();
// Given
var table = new Table();
table.AddColumn("Column #1");
// When
var result = Record.Exception(() => table.Rows.Insert(0, null));
// When
table.Rows.Add(new[] { Text.Empty });
// Then
result.ShouldBeOfType<ArgumentNullException>()
.ParamName.ShouldBe("columns");
}
[Fact]
public void Should_Insert_Row()
{
// Given
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { Text.Empty });
// When
table.Rows.Insert(0, new[] { Text.Empty });
// Then
table.Rows.Count.ShouldBe(2);
}
[Fact]
public void Should_Return_Index_Of_Inserted_Row()
{
// Given
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
// When
var result = table.Rows.Insert(1, new[] { new Text("3") });
// Then
result.ShouldBe(1);
}
[Fact]
[Expectation("Insert")]
public Task Should_Insert_Item_At_Correct_Place()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Insert(1, new[] { new Text("3") });
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
// Then
table.Rows.Count.ShouldBe(1);
}
[UsesVerify]
public sealed class TheRemoveMethod
[Fact]
public void Should_Return_Index_Of_Added_Row()
{
[Fact]
public void Should_Throw_If_Index_Is_Negative()
{
// Given
var table = new Table();
table.AddColumn("Column #1");
// Given
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { Text.Empty });
// When
var result = Record.Exception(() => table.Rows.RemoveAt(-1));
// When
var result = table.Rows.Add(new[] { Text.Empty });
// Then
result.ShouldBeOfType<IndexOutOfRangeException>()
.Message.ShouldBe("Table row index cannot be negative.");
}
[Fact]
public void Should_Throw_If_Index_Is_Larger_Than_Number_Of_Rows()
{
// Given
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3") });
// When
var result = Record.Exception(() => table.Rows.RemoveAt(3));
// Then
result.ShouldBeOfType<IndexOutOfRangeException>()
.Message.ShouldBe("Table row index cannot exceed the number of rows in the table.");
}
[Fact]
[Expectation("Remove")]
public Task Should_Remove_Row()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3") });
table.Rows.RemoveAt(1);
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
// Then
result.ShouldBe(1);
}
public sealed class TheClearMethod
[Fact]
[Expectation("Add")]
public Task Should_Add_Item_At_Correct_Place()
{
[Fact]
public void Should_Remove_All_Rows()
{
// Given
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3") });
table.Rows.Clear();
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3") });
// When
var result = table.Rows.Count;
// When
console.Write(table);
// Then
result.ShouldBe(0);
}
}
[UsesVerify]
public sealed class TheUpdateMethod
{
[Fact]
public Task Should_Update_Row_With_String()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddColumn("Column #3");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
table.UpdateCell(2, 2, "5");
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
public Task Should_Update_Row_With_Renderable()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddColumn("Column #3");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
table.UpdateCell(2, 2, new Markup("5"));
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
public void Should_Throw_If_Index_Is_Larger_Than_Number_Of_Rows()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddColumn("Column #3");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
table.UpdateCell(2, 2, "5");
// When
var result = Record.Exception(() => table.UpdateCell(5, 2, "5"));
// Then
result.ShouldBeOfType<IndexOutOfRangeException>()
.Message.ShouldBe("Table row index cannot exceed the number of rows in the table.");
}
[Fact]
public void Should_Throw_If_Index_Is_Larger_Than_Number_Of_Columns()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddColumn("Column #3");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
table.UpdateCell(2, 2, "5");
// When
var result = Record.Exception(() => table.UpdateCell(2, 5, "5"));
// Then
result.ShouldBeOfType<IndexOutOfRangeException>()
.Message.ShouldBe("Table column index cannot exceed the number of rows in the table.");
}
[Fact]
public void Should_Throw_If_Index_Row_Is_Negative()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddColumn("Column #3");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
table.UpdateCell(2, 2, "5");
// When
var result = Record.Exception(() => table.UpdateCell(-1, 2, "5"));
// Then
result.ShouldBeOfType<IndexOutOfRangeException>()
.Message.ShouldBe("Table row index cannot be negative.");
}
[Fact]
public void Should_Throw_If_Index_Column_Is_Negative()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddColumn("Column #3");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
table.UpdateCell(2, 2, "5");
// When
var result = Record.Exception(() => table.UpdateCell(2, -1, "5"));
// Then
result.ShouldBeOfType<IndexOutOfRangeException>()
.Message.ShouldBe("Table column index cannot be negative.");
}
// Then
return Verifier.Verify(console.Output);
}
}
}
[UsesVerify]
public sealed class TheInsertMethod
{
[Fact]
public void Should_Throw_If_Columns_Are_Null()
{
// Given
var table = new Table();
// When
var result = Record.Exception(() => table.Rows.Insert(0, null));
// Then
result.ShouldBeOfType<ArgumentNullException>()
.ParamName.ShouldBe("columns");
}
[Fact]
public void Should_Insert_Row()
{
// Given
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { Text.Empty });
// When
table.Rows.Insert(0, new[] { Text.Empty });
// Then
table.Rows.Count.ShouldBe(2);
}
[Fact]
public void Should_Return_Index_Of_Inserted_Row()
{
// Given
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
// When
var result = table.Rows.Insert(1, new[] { new Text("3") });
// Then
result.ShouldBe(1);
}
[Fact]
[Expectation("Insert")]
public Task Should_Insert_Item_At_Correct_Place()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Insert(1, new[] { new Text("3") });
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
}
[UsesVerify]
public sealed class TheRemoveMethod
{
[Fact]
public void Should_Throw_If_Index_Is_Negative()
{
// Given
var table = new Table();
table.AddColumn("Column #1");
// When
var result = Record.Exception(() => table.Rows.RemoveAt(-1));
// Then
result.ShouldBeOfType<IndexOutOfRangeException>()
.Message.ShouldBe("Table row index cannot be negative.");
}
[Fact]
public void Should_Throw_If_Index_Is_Larger_Than_Number_Of_Rows()
{
// Given
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3") });
// When
var result = Record.Exception(() => table.Rows.RemoveAt(3));
// Then
result.ShouldBeOfType<IndexOutOfRangeException>()
.Message.ShouldBe("Table row index cannot exceed the number of rows in the table.");
}
[Fact]
[Expectation("Remove")]
public Task Should_Remove_Row()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3") });
table.Rows.RemoveAt(1);
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
}
public sealed class TheClearMethod
{
[Fact]
public void Should_Remove_All_Rows()
{
// Given
var table = new Table();
table.AddColumn("Column #1");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3") });
table.Rows.Clear();
// When
var result = table.Rows.Count;
// Then
result.ShouldBe(0);
}
}
[UsesVerify]
public sealed class TheUpdateMethod
{
[Fact]
public Task Should_Update_Row_With_String()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddColumn("Column #3");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
table.UpdateCell(2, 2, "5");
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
public Task Should_Update_Row_With_Renderable()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddColumn("Column #3");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
table.UpdateCell(2, 2, new Markup("5"));
// When
console.Write(table);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
public void Should_Throw_If_Index_Is_Larger_Than_Number_Of_Rows()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddColumn("Column #3");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
table.UpdateCell(2, 2, "5");
// When
var result = Record.Exception(() => table.UpdateCell(5, 2, "5"));
// Then
result.ShouldBeOfType<IndexOutOfRangeException>()
.Message.ShouldBe("Table row index cannot exceed the number of rows in the table.");
}
[Fact]
public void Should_Throw_If_Index_Is_Larger_Than_Number_Of_Columns()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddColumn("Column #3");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
table.UpdateCell(2, 2, "5");
// When
var result = Record.Exception(() => table.UpdateCell(2, 5, "5"));
// Then
result.ShouldBeOfType<IndexOutOfRangeException>()
.Message.ShouldBe("Table column index cannot exceed the number of rows in the table.");
}
[Fact]
public void Should_Throw_If_Index_Row_Is_Negative()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddColumn("Column #3");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
table.UpdateCell(2, 2, "5");
// When
var result = Record.Exception(() => table.UpdateCell(-1, 2, "5"));
// Then
result.ShouldBeOfType<IndexOutOfRangeException>()
.Message.ShouldBe("Table row index cannot be negative.");
}
[Fact]
public void Should_Throw_If_Index_Column_Is_Negative()
{
// Given
var console = new TestConsole();
var table = new Table();
table.AddColumn("Column #1");
table.AddColumn("Column #2");
table.AddColumn("Column #3");
table.Rows.Add(new[] { new Text("1") });
table.Rows.Add(new[] { new Text("2") });
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
table.UpdateCell(2, 2, "5");
// When
var result = Record.Exception(() => table.UpdateCell(2, -1, "5"));
// Then
result.ShouldBeOfType<IndexOutOfRangeException>()
.Message.ShouldBe("Table column index cannot be negative.");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,158 +1,152 @@
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 TextTests
{
public sealed class TextTests
public sealed class TheLengthProperty
{
public sealed class TheLengthProperty
{
[Theory]
[InlineData("Hello", 5)]
[InlineData("Hello\nWorld", 11)]
public void Should_Return_The_Number_Of_Characters(string input, int expected)
{
// Given
var markup = new Text(input);
// When
var result = markup.Length;
// Then
result.ShouldBe(expected);
}
}
public sealed class TheLinesProperty
{
[Theory]
[InlineData("Hello", 1)]
[InlineData("Hello\nWorld", 2)]
public void Should_Return_The_Number_Of_Lines(string input, int expected)
{
// Given
var markup = new Text(input);
// When
var result = markup.Lines;
// Then
result.ShouldBe(expected);
}
}
[Fact]
public void Should_Consider_The_Longest_Word_As_Minimum_Width()
{
// Given
var caps = new TestCapabilities { Unicode = true };
var text = new Text("Foo Bar Baz\nQux\nLol mobile");
// When
var result = ((IRenderable)text).Measure(caps.CreateRenderContext(), 80);
// Then
result.Min.ShouldBe(6);
}
[Fact]
public void Should_Consider_The_Longest_Line_As_Maximum_Width()
{
// Given
var caps = new TestCapabilities { Unicode = true };
var text = new Text("Foo Bar Baz\nQux\nLol mobile");
// When
var result = ((IRenderable)text).Measure(caps.CreateRenderContext(), 80);
// Then
result.Max.ShouldBe(11);
}
[Fact]
public void Should_Render_Unstyled_Text_As_Expected()
{
// Given
var console = new TestConsole();
var text = new Text("Hello World");
// When
console.Write(text);
// Then
console.Output.ShouldBe("Hello World");
}
[Theory]
[InlineData("Hello\n\nWorld\n\n")]
[InlineData("Hello\r\n\r\nWorld\r\n\r\n")]
public void Should_Write_Line_Breaks(string input)
[InlineData("Hello", 5)]
[InlineData("Hello\nWorld", 11)]
public void Should_Return_The_Number_Of_Characters(string input, int expected)
{
// Given
var console = new TestConsole();
var text = new Text(input);
var markup = new Text(input);
// When
console.Write(text);
var result = markup.Length;
// Then
console.Output.ShouldBe("Hello\n\nWorld\n\n");
}
[Fact]
public void Should_Render_Panel_2()
{
// Given
var console = new TestConsole();
// When
console.Write(new Markup("[b]Hello World[/]\n[yellow]Hello World[/]"));
// Then
console.Lines.Count.ShouldBe(2);
console.Lines[0].ShouldBe("Hello World");
console.Lines[1].ShouldBe("Hello World");
}
[Theory]
[InlineData(5, "Hello World", "Hello\nWorld")]
[InlineData(10, "Hello Sweet Nice World", "Hello \nSweet Nice\nWorld")]
public void Should_Split_Unstyled_Text_To_New_Lines_If_Width_Exceeds_Console_Width(
int width, string input, string expected)
{
// Given
var console = new TestConsole().Width(width);
var text = new Text(input);
// When
console.Write(text);
// Then
console.Output
.NormalizeLineEndings()
.ShouldBe(expected);
}
[Theory]
[InlineData(Overflow.Fold, "foo \npneumonoultram\nicroscopicsili\ncovolcanoconio\nsis bar qux")]
[InlineData(Overflow.Crop, "foo \npneumonoultram\nbar qux")]
[InlineData(Overflow.Ellipsis, "foo \npneumonoultra…\nbar qux")]
public void Should_Overflow_Text_Correctly(Overflow overflow, string expected)
{
// Given
var console = new TestConsole().Width(14);
var text = new Text("foo pneumonoultramicroscopicsilicovolcanoconiosis bar qux")
.Overflow(overflow);
// When
console.Write(text);
// Then
console.Output
.NormalizeLineEndings()
.ShouldBe(expected);
result.ShouldBe(expected);
}
}
}
public sealed class TheLinesProperty
{
[Theory]
[InlineData("Hello", 1)]
[InlineData("Hello\nWorld", 2)]
public void Should_Return_The_Number_Of_Lines(string input, int expected)
{
// Given
var markup = new Text(input);
// When
var result = markup.Lines;
// Then
result.ShouldBe(expected);
}
}
[Fact]
public void Should_Consider_The_Longest_Word_As_Minimum_Width()
{
// Given
var caps = new TestCapabilities { Unicode = true };
var text = new Text("Foo Bar Baz\nQux\nLol mobile");
// When
var result = ((IRenderable)text).Measure(caps.CreateRenderContext(), 80);
// Then
result.Min.ShouldBe(6);
}
[Fact]
public void Should_Consider_The_Longest_Line_As_Maximum_Width()
{
// Given
var caps = new TestCapabilities { Unicode = true };
var text = new Text("Foo Bar Baz\nQux\nLol mobile");
// When
var result = ((IRenderable)text).Measure(caps.CreateRenderContext(), 80);
// Then
result.Max.ShouldBe(11);
}
[Fact]
public void Should_Render_Unstyled_Text_As_Expected()
{
// Given
var console = new TestConsole();
var text = new Text("Hello World");
// When
console.Write(text);
// Then
console.Output.ShouldBe("Hello World");
}
[Theory]
[InlineData("Hello\n\nWorld\n\n")]
[InlineData("Hello\r\n\r\nWorld\r\n\r\n")]
public void Should_Write_Line_Breaks(string input)
{
// Given
var console = new TestConsole();
var text = new Text(input);
// When
console.Write(text);
// Then
console.Output.ShouldBe("Hello\n\nWorld\n\n");
}
[Fact]
public void Should_Render_Panel_2()
{
// Given
var console = new TestConsole();
// When
console.Write(new Markup("[b]Hello World[/]\n[yellow]Hello World[/]"));
// Then
console.Lines.Count.ShouldBe(2);
console.Lines[0].ShouldBe("Hello World");
console.Lines[1].ShouldBe("Hello World");
}
[Theory]
[InlineData(5, "Hello World", "Hello\nWorld")]
[InlineData(10, "Hello Sweet Nice World", "Hello \nSweet Nice\nWorld")]
public void Should_Split_Unstyled_Text_To_New_Lines_If_Width_Exceeds_Console_Width(
int width, string input, string expected)
{
// Given
var console = new TestConsole().Width(width);
var text = new Text(input);
// When
console.Write(text);
// Then
console.Output
.NormalizeLineEndings()
.ShouldBe(expected);
}
[Theory]
[InlineData(Overflow.Fold, "foo \npneumonoultram\nicroscopicsili\ncovolcanoconio\nsis bar qux")]
[InlineData(Overflow.Crop, "foo \npneumonoultram\nbar qux")]
[InlineData(Overflow.Ellipsis, "foo \npneumonoultra…\nbar qux")]
public void Should_Overflow_Text_Correctly(Overflow overflow, string expected)
{
// Given
var console = new TestConsole().Width(14);
var text = new Text("foo pneumonoultramicroscopicsilicovolcanoconiosis bar qux")
.Overflow(overflow);
// When
console.Write(text);
// Then
console.Output
.NormalizeLineEndings()
.ShouldBe(expected);
}
}

View File

@ -1,85 +1,76 @@
using System.Linq;
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("Widgets/Tree")]
public class TreeTests
{
[UsesVerify]
[ExpectationPath("Widgets/Tree")]
public class TreeTests
[Fact]
[Expectation("Render")]
public Task Should_Render_Tree_Correctly()
{
[Fact]
[Expectation("Render")]
public Task Should_Render_Tree_Correctly()
{
// Given
var console = new TestConsole();
// Given
var console = new TestConsole();
var tree = new Tree(new Text("Root node")).Guide(TreeGuide.DoubleLine);
var tree = new Tree(new Text("Root node")).Guide(TreeGuide.DoubleLine);
var nestedChildren = Enumerable.Range(0, 10).Select(x => new Text($"multiple\nline {x}"));
var child2 = new TreeNode(new Text("child2"));
var child2Child = new TreeNode(new Text("child2-1"));
child2.AddNode(child2Child);
child2Child.AddNode(new TreeNode(new Text("Child2-1-1\nchild")));
var child3 = new TreeNode(new Text("child3"));
var child3Child = new TreeNode(new Text("single leaf\nmultiline"));
child3Child.AddNode(new TreeNode(new Calendar(2021, 01)));
child3.AddNode(child3Child);
var nestedChildren = Enumerable.Range(0, 10).Select(x => new Text($"multiple\nline {x}"));
var child2 = new TreeNode(new Text("child2"));
var child2Child = new TreeNode(new Text("child2-1"));
child2.AddNode(child2Child);
child2Child.AddNode(new TreeNode(new Text("Child2-1-1\nchild")));
var child3 = new TreeNode(new Text("child3"));
var child3Child = new TreeNode(new Text("single leaf\nmultiline"));
child3Child.AddNode(new TreeNode(new Calendar(2021, 01)));
child3.AddNode(child3Child);
tree.AddNode("child1").AddNodes(nestedChildren);
tree.AddNode(child2);
tree.AddNode(child3);
tree.AddNode("child4");
tree.AddNode("child1").AddNodes(nestedChildren);
tree.AddNode(child2);
tree.AddNode(child3);
tree.AddNode("child4");
// When
console.Write(tree);
// When
console.Write(tree);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Render_NoChildren")]
public Task Should_Render_Tree_With_No_Child_Nodes_Correctly()
{
// Given
var console = new TestConsole();
var tree = new Tree(new Text("Root node"));
// When
console.Write(tree);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
public void Should_Throw_If_Tree_Contains_Cycles()
{
// Given
var console = new TestConsole();
var child2 = new TreeNode(new Text("child 2"));
var child3 = new TreeNode(new Text("child 3"));
var child1 = new TreeNode(new Text("child 1"));
child1.AddNodes(child2, child3);
var root = new TreeNode(new Text("Branch Node"));
root.AddNodes(child1);
child2.AddNode(root);
var tree = new Tree("root node");
tree.AddNodes(root);
// When
var result = Record.Exception(() => console.Write(tree));
// Then
result.ShouldBeOfType<CircularTreeException>();
}
// Then
return Verifier.Verify(console.Output);
}
}
[Fact]
[Expectation("Render_NoChildren")]
public Task Should_Render_Tree_With_No_Child_Nodes_Correctly()
{
// Given
var console = new TestConsole();
var tree = new Tree(new Text("Root node"));
// When
console.Write(tree);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
public void Should_Throw_If_Tree_Contains_Cycles()
{
// Given
var console = new TestConsole();
var child2 = new TreeNode(new Text("child 2"));
var child3 = new TreeNode(new Text("child 3"));
var child1 = new TreeNode(new Text("child 1"));
child1.AddNodes(child2, child3);
var root = new TreeNode(new Text("Branch Node"));
root.AddNodes(child1);
child2.AddNode(root);
var tree = new Tree("root node");
tree.AddNodes(root);
// When
var result = Record.Exception(() => console.Write(tree));
// Then
result.ShouldBeOfType<CircularTreeException>();
}
}