Selkie.git | t/ | 07-text.rakutest


use Test;
use lib 'lib';

use Selkie::Align;
use Selkie::Widget::Text;
use Selkie::Sizing;
use Selkie::Style;

plan 14;

subtest "construction defaults" => {
    plan 2;
    my $t = Selkie::Widget::Text.new;
    is $t.text, '', "empty text by default";
    ok $t.is-dirty, "starts dirty";
};

subtest "construction with text" => {
    plan 1;
    my $t = Selkie::Widget::Text.new(text => 'hello');
    is $t.text, 'hello', "text set at construction";
};

subtest "theme-slot can be set at construction and changed later" => {
    plan 4;
    my $t = Selkie::Widget::Text.new(text => 'heading', theme-slot => 'overlay-title');
    is $t.theme-slot, 'overlay-title', "theme-slot set at construction";
    $t.clear-dirty;
    $t.set-theme-slot('text-dim');
    is $t.theme-slot, 'text-dim', "theme-slot changed";
    ok $t.is-dirty, "changing theme-slot marks dirty";
    $t.set-style(Selkie::Style.new(fg => 0x123456));
    ok $t.style.defined, "explicit style still accepted";
};

subtest "set-text updates text and marks dirty" => {
    plan 3;
    my $t = Selkie::Widget::Text.new;
    $t.clear-dirty;
    $t.set-text('new text');
    is $t.text, 'new text', "text updated";
    ok $t.is-dirty, "marked dirty";
    $t.set-text('another');
    is $t.text, 'another', "text updated again";
};

subtest "logical-height - single line within width" => {
    plan 1;
    my $t = Selkie::Widget::Text.new(text => 'hello');
    $t.resize(10, 80);
    is $t.logical-height, 1, "single line fits in 80 cols";
};

subtest "logical-height - multiple lines" => {
    plan 1;
    my $t = Selkie::Widget::Text.new(text => "line one\nline two\nline three");
    $t.resize(10, 80);
    is $t.logical-height, 3, "three newline-separated lines";
};

subtest "logical-height - word wrap at column boundary" => {
    plan 1;
    # 20 chars wrapped at 10 cols = 2 lines
    my $t = Selkie::Widget::Text.new(text => 'a' x 20);
    $t.resize(10, 10);
    is $t.logical-height, 2, "20 chars at width 10 wraps to 2 lines";
};

subtest "logical-height - exact fit no extra line" => {
    plan 1;
    my $t = Selkie::Widget::Text.new(text => 'a' x 10);
    $t.resize(10, 10);
    is $t.logical-height, 1, "10 chars at width 10 fits in 1 line";
};

subtest "logical-height - empty text" => {
    plan 1;
    my $t = Selkie::Widget::Text.new(text => '');
    $t.resize(10, 80);
    is $t.logical-height, 1, "empty text yields 1 line (empty line)";
};

subtest "word wrap - long line splits correctly" => {
    plan 1;
    # 25 chars at width 10 → ceil(25/10) = 3 lines
    my $t = Selkie::Widget::Text.new(text => 'a' x 25);
    $t.resize(10, 10);
    is $t.logical-height, 3, "25 chars at width 10 wraps to 3 lines";
};

subtest "word wrap - multiple lines with wrapping" => {
    plan 1;
    my $text = ('b' x 15) ~ "\n" ~ ('c' x 8);
    my $t = Selkie::Widget::Text.new(text => $text);
    $t.resize(10, 10);
    # First line: 15 chars → 2 wrapped lines (10 + 5)
    # Second line: 8 chars → 1 line
    is $t.logical-height, 3, "mixed wrapping across newlines";
};

subtest "resize triggers rewrap when width changes" => {
    plan 2;
    my $t = Selkie::Widget::Text.new(text => 'a' x 20);
    $t.resize(10, 20);
    is $t.logical-height, 1, "20 chars at width 20 = 1 line";
    $t.resize(10, 10);
    is $t.logical-height, 2, "20 chars at width 10 = 2 lines after resize";
};

subtest "resize does not rewrap when only height changes" => {
    plan 2;
    my $t = Selkie::Widget::Text.new(text => 'a' x 20);
    $t.resize(10, 10);
    is $t.logical-height, 2, "2 lines at width 10";
    $t.resize(20, 10);
    is $t.logical-height, 2, "still 2 lines after height-only change";
};

subtest "text is left-aligned unless asked otherwise" => {
    plan 5;
    # Back-compat proof for the alignment feature: an untouched Text
    # reports TextLeft, and every line — short, exact-fit, empty, or
    # over-long — starts at column 0, which is where Text has always
    # written them. t/90-text-align covers the other alignments.
    my $t = Selkie::Widget::Text.new(text => "hi\n\nlonger line here");
    $t.resize(5, 12);
    is $t.align, TextLeft, "default alignment is TextLeft";
    is Selkie::Widget::Text.align-column($t.align, 2,  $t.cols), 0, "short line at 0";
    is Selkie::Widget::Text.align-column($t.align, 12, $t.cols), 0, "exact fit at 0";
    is Selkie::Widget::Text.align-column($t.align, 0,  $t.cols), 0, "empty line at 0";
    is Selkie::Widget::Text.align-column($t.align, 40, $t.cols), 0, "over-long line at 0";
};