Add support for manipulating individual table rows

Closes #500
This commit is contained in:
Patrik Svensson 2021-08-05 22:14:19 +02:00 committed by Patrik Svensson
parent 57731c0d55
commit e169df6303
25 changed files with 833 additions and 59 deletions

View File

@ -89,7 +89,7 @@ jobs:
shell: bash
run: |
dotnet tool restore
dotnet example --all
dotnet example --all --skip live --skip livetable --skip prompt
- name: Build
shell: bash

View File

@ -15,7 +15,7 @@
]
},
"dotnet-example": {
"version": "1.3.1",
"version": "1.5.0",
"commands": [
"dotnet-example"
]

View File

@ -5,7 +5,7 @@
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Live</ExampleTitle>
<ExampleDescription>Demonstrates how to do live updates.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
<ExampleGroup>Live</ExampleGroup>
</PropertyGroup>
<ItemGroup>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>LiveTable</ExampleTitle>
<ExampleDescription>Demonstrates how to do live updates in a table.</ExampleDescription>
<ExampleGroup>Live</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,86 @@
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Spectre.Console.Examples
{
public static class Program
{
private const int NumberOfRows = 10;
private static readonly Random _random = new();
private static readonly string[] _exchanges = new string[]
{
"SGD", "SEK", "PLN",
"MYR", "EUR", "USD",
"AUD", "JPY", "CNH",
"HKD", "CAD", "INR",
"DKK", "GBP", "RUB",
"NZD", "MXN", "IDR",
"TWD", "THB", "VND",
};
public static async Task Main(string[] args)
{
var table = new Table().Expand().BorderColor(Color.Grey);
table.AddColumn("[yellow]Source currency[/]");
table.AddColumn("[yellow]Destination currency[/]");
table.AddColumn("[yellow]Exchange rate[/]");
AnsiConsole.MarkupLine("Press [yellow]CTRL+C[/] to exit");
await AnsiConsole.Live(table)
.AutoClear(false)
.Overflow(VerticalOverflow.Ellipsis)
.Cropping(VerticalOverflowCropping.Bottom)
.StartAsync(async ctx =>
{
// Add some initial rows
foreach (var _ in Enumerable.Range(0, NumberOfRows))
{
AddExchangeRateRow(table);
}
// Continously update the table
while (true)
{
// More rows than we want?
if (table.Rows.Count > NumberOfRows)
{
// Remove the first one
table.Rows.RemoveAt(0);
}
// Add a new row
AddExchangeRateRow(table);
// Refresh and wait for a while
ctx.Refresh();
await Task.Delay(400);
}
});
}
private static void AddExchangeRateRow(Table table)
{
var (source, destination, rate) = GetExchangeRate();
table.AddRow(
source, destination,
_random.NextDouble() > 0.35D ? $"[green]{rate}[/]" : $"[red]{rate}[/]");
}
private static (string Source, string Destination, double Rate) GetExchangeRate()
{
var source = _exchanges[_random.Next(0, _exchanges.Length)];
var dest = _exchanges[_random.Next(0, _exchanges.Length)];
var rate = 200 / ((_random.NextDouble() * 320) + 1);
while (source == dest)
{
dest = _exchanges[_random.Next(0, _exchanges.Length)];
}
return (source, dest, rate);
}
}
}

View File

@ -63,6 +63,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tables", "Console\Tables\Ta
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Trees", "Console\Trees\Trees.csproj", "{2BD88288-E05D-4978-B045-17937078E63C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiveTable", "Console\LiveTable\LiveTable.csproj", "{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -409,6 +411,18 @@ Global
{2BD88288-E05D-4978-B045-17937078E63C}.Release|x64.Build.0 = Release|Any CPU
{2BD88288-E05D-4978-B045-17937078E63C}.Release|x86.ActiveCfg = Release|Any CPU
{2BD88288-E05D-4978-B045-17937078E63C}.Release|x86.Build.0 = Release|Any CPU
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x64.ActiveCfg = Debug|Any CPU
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x64.Build.0 = Debug|Any CPU
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x86.ActiveCfg = Debug|Any CPU
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x86.Build.0 = Debug|Any CPU
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|Any CPU.Build.0 = Release|Any CPU
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x64.ActiveCfg = Release|Any CPU
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x64.Build.0 = Release|Any CPU
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x86.ActiveCfg = Release|Any CPU
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Spectre.Console.Rendering;
@ -35,6 +36,28 @@ namespace Spectre.Console
return table;
}
/// <summary>
/// Adds a row to the table.
/// </summary>
/// <param name="table">The table to add the row to.</param>
/// <param name="columns">The row columns to add.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table AddRow(this Table table, IEnumerable<IRenderable> columns)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (columns is null)
{
throw new ArgumentNullException(nameof(columns));
}
table.Rows.Add(new TableRow(columns));
return table;
}
/// <summary>
/// Adds a row to the table.
/// </summary>
@ -48,7 +71,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(table));
}
return table.AddRow(columns);
return table.AddRow((IEnumerable<IRenderable>)columns);
}
/// <summary>
@ -143,6 +166,80 @@ namespace Spectre.Console
return table;
}
/// <summary>
/// Inserts a row in the table at the specified index.
/// </summary>
/// <param name="table">The table to add the row to.</param>
/// <param name="index">The index to insert the row at.</param>
/// <param name="columns">The row columns to add.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table InsertRow(this Table table, int index, IEnumerable<IRenderable> columns)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (columns is null)
{
throw new ArgumentNullException(nameof(columns));
}
table.Rows.Insert(index, new TableRow(columns));
return table;
}
/// <summary>
/// Inserts a row in the table at the specified index.
/// </summary>
/// <param name="table">The table to add the row to.</param>
/// <param name="index">The index to insert the row at.</param>
/// <param name="columns">The row columns to add.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table InsertRow(this Table table, int index, params IRenderable[] columns)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
return InsertRow(table, index, (IEnumerable<IRenderable>)columns);
}
/// <summary>
/// Inserts a row in the table at the specified index.
/// </summary>
/// <param name="table">The table to add the row to.</param>
/// <param name="index">The index to insert the row at.</param>
/// <param name="columns">The row columns to add.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table InsertRow(this Table table, int index, params string[] columns)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
return InsertRow(table, index, columns.Select(column => new Markup(column)));
}
/// <summary>
/// Removes a row from the table with the specified index.
/// </summary>
/// <param name="table">The table to add the row to.</param>
/// <param name="index">The index to remove the row at.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table RemoveRow(this Table table, int index)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Rows.RemoveAt(index);
return table;
}
/// <summary>
/// Sets the table width.
/// </summary>

View File

@ -11,7 +11,6 @@ namespace Spectre.Console
public sealed class Table : Renderable, IHasTableBorder, IExpandable, IAlignable
{
private readonly List<TableColumn> _columns;
private readonly List<TableRow> _rows;
/// <summary>
/// Gets the table columns.
@ -21,7 +20,7 @@ namespace Spectre.Console
/// <summary>
/// Gets the table rows.
/// </summary>
public IReadOnlyList<TableRow> Rows => _rows;
public TableRowCollection Rows { get; }
/// <inheritdoc/>
public TableBorder Border { get; set; } = TableBorder.Square;
@ -81,7 +80,7 @@ namespace Spectre.Console
public Table()
{
_columns = new List<TableColumn>();
_rows = new List<TableRow>();
Rows = new TableRowCollection(this);
}
/// <summary>
@ -96,7 +95,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(column));
}
if (_rows.Count > 0)
if (Rows.Count > 0)
{
throw new InvalidOperationException("Cannot add new columns to table with existing rows.");
}
@ -105,36 +104,6 @@ namespace Spectre.Console
return this;
}
/// <summary>
/// Adds a row to the table.
/// </summary>
/// <param name="columns">The row columns to add.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public Table AddRow(IEnumerable<IRenderable> columns)
{
if (columns is null)
{
throw new ArgumentNullException(nameof(columns));
}
var rowColumnCount = columns.GetCount();
if (rowColumnCount > _columns.Count)
{
throw new InvalidOperationException("The number of row columns are greater than the number of table columns.");
}
_rows.Add(new TableRow(columns));
// Need to add missing columns?
if (rowColumnCount < _columns.Count)
{
var diff = _columns.Count - rowColumnCount;
Enumerable.Range(0, diff).ForEach(_ => _rows.Last().Add(Text.Empty));
}
return this;
}
/// <inheritdoc/>
protected override Measurement Measure(RenderContext context, int maxWidth)
{
@ -190,7 +159,7 @@ namespace Spectre.Console
}
// Add rows
rows.AddRange(_rows);
rows.AddRange(Rows);
// Show footers?
if (ShowFooters && _columns.Any(c => c.Footer != null))

View File

@ -12,6 +12,11 @@ namespace Spectre.Console
{
private readonly List<IRenderable> _items;
/// <summary>
/// Gets the number of columns in the row.
/// </summary>
public int Count => _items.Count;
internal bool IsHeader { get; }
internal bool IsFooter { get; }

View File

@ -0,0 +1,160 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// Represents a collection holding table rows.
/// </summary>
public sealed class TableRowCollection : IReadOnlyList<TableRow>
{
private readonly Table _table;
private readonly IList<TableRow> _list;
private readonly object _lock;
/// <inheritdoc/>
TableRow IReadOnlyList<TableRow>.this[int index]
{
get
{
lock (_lock)
{
return _list[index];
}
}
}
/// <summary>
/// Gets the number of rows in the collection.
/// </summary>
public int Count
{
get
{
lock (_lock)
{
return _list.Count;
}
}
}
internal TableRowCollection(Table table)
{
_table = table ?? throw new ArgumentNullException(nameof(table));
_list = new List<TableRow>();
_lock = new object();
}
/// <summary>
/// Adds a new row.
/// </summary>
/// <param name="columns">The columns that are part of the row to add.</param>
/// <returns>The index of the added item.</returns>
public int Add(IEnumerable<IRenderable> columns)
{
if (columns is null)
{
throw new ArgumentNullException(nameof(columns));
}
lock (_lock)
{
var row = CreateRow(columns);
_list.Add(row);
return _list.IndexOf(row);
}
}
/// <summary>
/// Inserts a new row at the specified index.
/// </summary>
/// <param name="index">The index to insert the row at.</param>
/// <param name="columns">The columns that are part of the row to insert.</param>
/// <returns>The index of the inserted item.</returns>
public int Insert(int index, IEnumerable<IRenderable> columns)
{
if (columns is null)
{
throw new ArgumentNullException(nameof(columns));
}
lock (_lock)
{
var row = CreateRow(columns);
_list.Insert(index, row);
return _list.IndexOf(row);
}
}
/// <summary>
/// Removes a row at the specified index.
/// </summary>
/// <param name="index">The index to remove a row at.</param>
public void RemoveAt(int index)
{
lock (_lock)
{
if (index < 0)
{
throw new IndexOutOfRangeException("Table row index cannot be negative.");
}
else if (index >= _list.Count)
{
throw new IndexOutOfRangeException("Table row index cannot exceed the number of rows in the table.");
}
_list.RemoveAt(index);
}
}
/// <summary>
/// Clears all rows.
/// </summary>
public void Clear()
{
lock (_lock)
{
_list.Clear();
}
}
/// <inheritdoc/>
public IEnumerator<TableRow> GetEnumerator()
{
lock (_lock)
{
var items = new TableRow[_list.Count];
_list.CopyTo(items, 0);
return new TableRowEnumerator(items);
}
}
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private TableRow CreateRow(IEnumerable<IRenderable> columns)
{
var row = new TableRow(columns);
if (row.Count > _table.Columns.Count)
{
throw new InvalidOperationException("The number of row columns are greater than the number of table columns.");
}
// Need to add missing columns
if (row.Count < _table.Columns.Count)
{
var diff = _table.Columns.Count - row.Count;
Enumerable.Range(0, diff).ForEach(_ => row.Add(Text.Empty));
}
return row;
}
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Spectre.Console
{
internal sealed class TableRowEnumerator : IEnumerator<TableRow>
{
private readonly TableRow[] _items;
private int _index;
public TableRow Current => _items[_index];
object? IEnumerator.Current => _items[_index];
public TableRowEnumerator(TableRow[] items)
{
_items = items ?? throw new ArgumentNullException(nameof(items));
_index = -1;
}
public void Dispose()
{
}
public bool MoveNext()
{
_index++;
return _index < _items.Length;
}
public void Reset()
{
_index = -1;
}
}
}

View File

@ -0,0 +1,7 @@
┌───────────┐
│ Column #1 │
├───────────┤
│ 1 │
│ 2 │
│ 3 │
└───────────┘

View File

@ -0,0 +1,7 @@
┌───────────┬───────────┐
│ Column #1 │ Column #2 │
├───────────┼───────────┤
│ 1 │ 1-2 │
│ 2 │ 2-2 │
│ 3 │ 3-2 │
└───────────┴───────────┘

View File

@ -0,0 +1,7 @@
┌───────────┬───────────┐
│ Column #1 │ Column #2 │
├───────────┼───────────┤
│ 1 │ 1-2 │
│ 2 │ 2-2 │
│ 3 │ 3-2 │
└───────────┴───────────┘

View File

@ -0,0 +1,7 @@
┌───────────┬───────────┐
│ Column #1 │ Column #2 │
├───────────┼───────────┤
│ 1 │ 1-2 │
│ 3 │ 3-2 │
│ 2 │ 2-2 │
└───────────┴───────────┘

View File

@ -0,0 +1,7 @@
┌───────────┬───────────┐
│ Column #1 │ Column #2 │
├───────────┼───────────┤
│ 1 │ 1-2 │
│ 3 │ 3-2 │
│ 2 │ 2-2 │
└───────────┴───────────┘

View File

@ -0,0 +1,6 @@
┌───────────┬───────────┐
│ Column #1 │ Column #2 │
├───────────┼───────────┤
│ 1 │ 1-2 │
│ 3 │ 3-2 │
└───────────┴───────────┘

View File

@ -0,0 +1,7 @@
┌───────────┐
│ Column #1 │
├───────────┤
│ 1 │
│ 3 │
│ 2 │
└───────────┘

View File

@ -0,0 +1,6 @@
┌───────────┐
│ Column #1 │
├───────────┤
│ 1 │
│ 3 │
└───────────┘

View File

@ -37,6 +37,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Expectations\Widgets\Table\Rows\Extensions\" />
<Folder Include="Expectations\Widgets\Tree\" />
</ItemGroup>

View File

@ -1,5 +1,3 @@
using System;
using System.Collections.Generic;
using Shouldly;
using Spectre.Console.Testing;
using Spectre.Console.Tests.Data;

View File

@ -0,0 +1,131 @@
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
{
[UsesVerify]
[ExpectationPath("Widgets/Table/Rows/Extensions")]
public sealed class TableRowCollectionExtensionsTests
{
[UsesVerify]
public sealed class TheAddRowMethod
{
[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") });
// 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);
}
}
[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

@ -0,0 +1,226 @@
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
{
[ExpectationPath("Widgets/Table/Rows")]
public sealed class TableRowCollectionTests
{
[UsesVerify]
public sealed class TheAddMethod
{
[Fact]
public void Should_Throw_If_Columns_Are_Null()
{
// Given
var table = new Table();
// 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);
}
}
[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);
}
}
}
}

View File

@ -78,20 +78,6 @@ namespace Spectre.Console.Tests.Unit
.ParamName.ShouldBe("columns");
}
[Fact]
public void Should_Throw_If_Renderable_Rows_Are_Null()
{
// Given
var table = new Table();
// When
var result = Record.Exception(() => table.AddRow(null));
// Then
result.ShouldBeOfType<ArgumentNullException>()
.ParamName.ShouldBe("columns");
}
[Fact]
public void Should_Add_Empty_Items_If_User_Provides_Less_Row_Items_Than_Columns()
{

View File

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Spectre.Console.Tests
{