Files
ttui/test/TinyTUI.Tests/Text/TerminalTextMeasurerTests.cs
T
chuan c435df9fcd
ci / restore build format test pack (push) Failing after 22s
test: converge smoke checks into xunit suite
- migrate remaining text and component smoke assertions into TinyTUI.Tests

- remove legacy TextChecks and ComponentChecks projects from solution

- update README test guidance to use the unified xUnit suite
2026-06-04 10:15:57 +08:00

68 lines
3.2 KiB
C#

using TinyTUI.Text;
namespace TinyTUI.Tests.Text;
public sealed class TerminalTextMeasurerTests
{
private readonly TerminalTextMeasurer _measurer = new();
[Fact]
public void GetWidthHandlesAnsiTabsAndEmojiGraphemes()
{
Assert.Equal(5, _measurer.GetWidth("\t\e[31m界\e[0m"));
Assert.Equal(2, _measurer.GetWidth("🇨"));
Assert.Equal(2, _measurer.GetWidth("🇨🇳"));
Assert.Equal(2, _measurer.GetWidth("👨‍💻"));
Assert.Equal(2, _measurer.GetWidth("⚡️"));
}
[Fact]
public void SliceTracksTabColumnsAndMeasuredWidth()
{
var strictTabSlice = _measurer.Slice("out 192M\t.pi/skill-tests/results-ha", 0, 10, strict: true);
Assert.Equal("out 192M", strictTabSlice.Text);
Assert.Equal(8, strictTabSlice.Width);
Assert.Equal(_measurer.GetWidth(strictTabSlice.Text), strictTabSlice.Width);
var afterTabSlice = _measurer.Slice("out 192M\t.pi/skill-tests/results-ha", 13, 10, strict: true);
Assert.Equal("i/skill-te", afterTabSlice.Text);
Assert.Equal(_measurer.GetWidth(afterTabSlice.Text), afterTabSlice.Width);
}
[Fact]
public void TruncateKeepsAnsiPrefixAndBracketsEllipsis()
{
var ansiText = $"\e[31m{string.Concat(Enumerable.Repeat("hello ", 100))}\e[0m";
var truncated = _measurer.Truncate(ansiText, 20, "…");
Assert.True(_measurer.GetWidth(truncated) <= 20);
Assert.Contains("\e[31m", truncated, StringComparison.Ordinal);
Assert.EndsWith("\e[0m…\e[0m", truncated, StringComparison.Ordinal);
Assert.Equal("\e[0m🙂\e[0m", _measurer.Truncate("abcdef", 2, "🙂"));
Assert.Equal(string.Empty, _measurer.Truncate("abcdef", 1, "🙂"));
}
[Fact]
public void WrapRestoresAnsiAndOsc8AcrossLines()
{
var plainWrapped = _measurer.Wrap("hello world this is a test", 10);
Assert.True(plainWrapped.Count > 1);
Assert.All(plainWrapped, line => Assert.True(_measurer.GetWidth(line) <= 10));
var redWrapped = _measurer.Wrap($"\e[31mhello world this is red\e[0m", 10);
Assert.All(redWrapped.Skip(1), line => Assert.StartsWith("\e[31m", line, StringComparison.Ordinal));
Assert.All(redWrapped.Take(redWrapped.Count - 1), line => Assert.False(line.EndsWith("\e[0m", StringComparison.Ordinal)));
var underlinedUrl = _measurer.Wrap($"read this thread \e[4mhttps://example.com/very/long/path/that/will/definitely/wrap\e[24m", 40);
Assert.Equal("read this thread", underlinedUrl[0]);
Assert.StartsWith("\e[4m", underlinedUrl[1], StringComparison.Ordinal);
Assert.Contains(underlinedUrl.Take(underlinedUrl.Count - 1), line => line.EndsWith("\e[24m", StringComparison.Ordinal));
var hyperlink = "\e]8;;https://example.com\a0123456789\e]8;;\a";
var hyperlinkWrapped = _measurer.Wrap(hyperlink, 6);
Assert.True(hyperlinkWrapped.Count > 1);
Assert.All(hyperlinkWrapped, line => Assert.Contains("\e]8;;https://example.com\a", line, StringComparison.Ordinal));
Assert.All(hyperlinkWrapped.Take(hyperlinkWrapped.Count - 1), line => Assert.EndsWith("\e]8;;\a", line, StringComparison.Ordinal));
}
}