BarefootJS

 view release on metacpan or  search on metacpan

t/template_primitives.t  view on Meta::CPAN

    is $bf->repeat('x',  1),  'x',      'once → unchanged';
    is $bf->repeat('ab', 0),  '',       'zero → empty';
    is $bf->repeat('ab', -2), '',       'negative → empty (JS throws; SSR degrades)';
    is $bf->repeat('ab', 2.9),'abab',   'fractional count truncates toward zero';
    is $bf->repeat('',   5),  '',       'empty receiver → empty';
    is $bf->repeat(undef, 3), '',       'undef receiver → empty';
};

# `String.prototype.padStart` / `padEnd` (#1448 Tier B) — pad to a
# target width with a repeat-and-truncate fill (default pad = space).
# Mirrors Go's rune-based `bf_pad_*`.
subtest 'pad_start / pad_end — pad to target width' => sub {
    is $bf->pad_start('42', 5, '0'), '00042', 'left-pad with 0';
    is $bf->pad_end('42', 5, '.'),   '42...', 'right-pad with .';

    # Default pad is a single space when omitted.
    is $bf->pad_start('42', 5),      '   42', 'default pad = space (start)';
    is $bf->pad_end('42', 5),        '42   ', 'default pad = space (end)';

    # Multi-char pad repeated then truncated to fill.
    is $bf->pad_start('x', 5, 'ab'), 'ababx', 'multi-char pad truncated (start)';
    is $bf->pad_end('x', 5, 'ab'),   'xabab', 'multi-char pad truncated (end)';

    # Already >= target, or empty pad → unchanged.
    is $bf->pad_start('hello', 3, '0'), 'hello', 'already >= target → unchanged';
    is $bf->pad_start('42', 5, ''),     '42',    'empty pad → unchanged';

    # Target truncates toward zero; undef receiver coerces to empty.
    is $bf->pad_start('7', 4.9, '0'),   '0007',  'fractional target truncates';
    is $bf->pad_start(undef, 3, '0'),   '000',   'undef receiver pads from empty';
};

# `Array.prototype.sort(cmp)` / `Array.prototype.toSorted(cmp)`
# lowering (#1448 Tier B). The opts hash-ref carries a `keys` list of
# the structured comparison keys the compiler extracted at parse time
# — adapter-side emit is a single call shape; the runtime walks the
# keys in priority order, branching on key_kind / compare_type /
# direction per key.
subtest 'sort — structured comparator dispatch' => sub {
    my $items = [
        { name => 'c', price => 30 },
        { name => 'a', price => 10 },
        { name => 'b', price => 20 },
    ];

    # Numeric field, ascending — the canonical struct-field case.
    is $bf->sort($items, { keys => [{ key_kind => 'field', key => 'price', compare_type => 'numeric', direction => 'asc' }] }),
        [ { name => 'a', price => 10 }, { name => 'b', price => 20 }, { name => 'c', price => 30 } ],
        'numeric field asc';

    # Numeric field, descending — reverse direction.
    is $bf->sort($items, { keys => [{ key_kind => 'field', key => 'price', compare_type => 'numeric', direction => 'desc' }] }),
        [ { name => 'c', price => 30 }, { name => 'b', price => 20 }, { name => 'a', price => 10 } ],
        'numeric field desc';

    # Primitive numeric — `(a, b) => a - b` shape (key_kind=self).
    is $bf->sort([3, 1, 2], { keys => [{ key_kind => 'self', compare_type => 'numeric', direction => 'asc' }] }),
        [1, 2, 3],
        'numeric self asc';

    # Primitive string — `(a, b) => a.localeCompare(b)`. The Perl
    # helper falls back to plain `cmp` (byte-ordering); within the
    # same case it matches lexicographic order.
    is $bf->sort(['charlie', 'alice', 'bob'], { keys => [{ key_kind => 'self', compare_type => 'string', direction => 'asc' }] }),
        ['alice', 'bob', 'charlie'],
        'string self asc';

    is $bf->sort(['charlie', 'alice', 'bob'], { keys => [{ key_kind => 'self', compare_type => 'string', direction => 'desc' }] }),
        ['charlie', 'bob', 'alice'],
        'string self desc';

    # Stable sort: equal keys preserve relative order. Perl `sort`
    # has been stable since 5.8; pinning the guarantee so a future
    # `use sort` pragma swap surfaces here.
    my $stable_input = [
        { name => 'first',  price => 10 },
        { name => 'second', price => 10 },
        { name => 'third',  price => 10 },
    ];
    is $bf->sort($stable_input, { keys => [{ key_kind => 'field', key => 'price', compare_type => 'numeric', direction => 'asc' }] }),
        $stable_input,
        'stable sort preserves equal-key order';

    # Mutation isolation: caller's source array must survive.
    my $src = [{ price => 3 }, { price => 1 }, { price => 2 }];
    my $out = $bf->sort($src, { keys => [{ key_kind => 'field', key => 'price', compare_type => 'numeric', direction => 'asc' }] });
    push @$out, { price => 99 };
    is $src, [{ price => 3 }, { price => 1 }, { price => 2 }],
        'source unchanged after mutating sort result';

    # Non-array receivers fall back to empty.
    is $bf->sort(undef, { keys => [{ key_kind => 'self', compare_type => 'numeric', direction => 'asc' }] }), [], 'undef receiver → []';
    is $bf->sort('not an array', { keys => [{ key_kind => 'self', compare_type => 'numeric', direction => 'asc' }] }), [], 'scalar receiver → []';
    is $bf->sort({a => 1}, { keys => [{ key_kind => 'self', compare_type => 'numeric', direction => 'asc' }] }), [], 'hash ref receiver → []';

    # Empty array short-circuits.
    is $bf->sort([], { keys => [{ key_kind => 'field', key => 'price', compare_type => 'numeric', direction => 'asc' }] }), [], 'empty array → []';
};

# Multi-key (`||`-chained) comparator: a tie on the primary key falls
# through to the next key. (#1448 Tier B follow-up.)
subtest 'sort — multi-key (||-chain) tie-breaks' => sub {
    # `(a,b) => a.p - b.p || a.name.localeCompare(b.name)`.
    my $items = [
        { p => 1, name => 'b' },
        { p => 1, name => 'a' },
        { p => 0, name => 'c' },
    ];
    is $bf->sort($items, { keys => [
            { key_kind => 'field', key => 'p',    compare_type => 'numeric', direction => 'asc' },
            { key_kind => 'field', key => 'name', compare_type => 'string',  direction => 'asc' },
        ] }),
        [ { p => 0, name => 'c' }, { p => 1, name => 'a' }, { p => 1, name => 'b' } ],
        'tie on primary key broken by secondary asc';

    # Descending secondary key: all primaries tie, so price desc orders.
    my $items2 = [
        { name => 'a', price => 10 },
        { name => 'a', price => 30 },
        { name => 'a', price => 20 },
    ];
    is $bf->sort($items2, { keys => [
            { key_kind => 'field', key => 'name',  compare_type => 'string',  direction => 'asc' },
            { key_kind => 'field', key => 'price', compare_type => 'numeric', direction => 'desc' },
        ] }),
        [ { name => 'a', price => 30 }, { name => 'a', price => 20 }, { name => 'a', price => 10 } ],
        'all primary tie → secondary price desc';
};

# `compare_type => 'auto'` (relational-ternary lowering): numeric when
# both keys look like numbers, else lexical. Mirrors Go's `bf_sort`.
subtest 'sort — auto compare (relational ternary)' => sub {
    is $bf->sort([3, 1, 2], { keys => [{ key_kind => 'self', compare_type => 'auto', direction => 'asc' }] }),
        [1, 2, 3],
        'auto numeric asc';

    is $bf->sort(['charlie', 'alice', 'bob'], { keys => [{ key_kind => 'self', compare_type => 'auto', direction => 'asc' }] }),
        ['alice', 'bob', 'charlie'],
        'auto non-numeric strings → lexical';

    # Numeric strings parse as numbers under auto (Go/Perl parity):
    # "10" sorts after "9", not lexically before it.
    is $bf->sort(['10', '9', '100'], { keys => [{ key_kind => 'self', compare_type => 'auto', direction => 'asc' }] }),
        ['9', '10', '100'],
        'auto numeric strings compare numerically';
};

done_testing;



( run in 0.521 second using v1.01-cache-2.11-cpan-a5162978ef8 )