Litavis
view release on metacpan or search on metacpan
t/03-cascade.t view on Meta::CPAN
use warnings;
use Test::More;
use_ok('Litavis');
# Strategy constants
use constant {
DEDUPE_OFF => 0,
DEDUPE_CONSERVATIVE => 1,
DEDUPE_AGGRESSIVE => 2,
};
# ââ Same-selector merging ââââââââââââââââââââââââââââââââââââ
{
my $d = Litavis->new;
$d->parse('.a { color: red; }');
$d->parse('.a { background: blue; }');
$d->_dedupe(DEDUPE_CONSERVATIVE);
is($d->_ast_rule_count, 1, 'same-sel merge: collapsed to one rule');
is($d->_ast_get_prop('.a', 'color'), 'red', 'same-sel merge: keeps first prop');
is($d->_ast_get_prop('.a', 'background'), 'blue', 'same-sel merge: adds second prop');
}
# ââ Same-selector: later value wins on conflict ââââââââââââââ
{
my $d = Litavis->new;
$d->parse('.a { color: red; }');
$d->parse('.a { color: blue; }');
$d->_dedupe(DEDUPE_CONSERVATIVE);
is($d->_ast_rule_count, 1, 'same-sel conflict: one rule');
is($d->_ast_get_prop('.a', 'color'), 'blue', 'same-sel conflict: later value wins');
}
# ââ Conservative: merge identical props, no conflict âââââââââ
{
my $d = Litavis->new;
$d->parse('
.a { color: red; padding: 8px; }
.b { font-size: 16px; }
.c { color: red; padding: 8px; }
');
$d->_dedupe(DEDUPE_CONSERVATIVE);
is($d->_ast_rule_count, 2, 'conservative safe merge: 3 â 2');
is($d->_ast_rule_selector(0), '.a, .c', 'conservative safe merge: selectors joined');
is($d->_ast_rule_selector(1), '.b', 'conservative safe merge: .b kept');
}
# ââ Conservative: DO NOT merge when intervening conflict âââââ
{
my $d = Litavis->new;
$d->parse('
.reset { color: black; padding: 8px; }
.theme { color: red; }
.override { color: black; padding: 8px; }
');
$d->_dedupe(DEDUPE_CONSERVATIVE);
is($d->_ast_rule_count, 3, 'conservative cascade: no merge (intervening color conflict)');
is($d->_ast_rule_selector(0), '.reset', 'conservative cascade: .reset stays at 0');
is($d->_ast_rule_selector(1), '.theme', 'conservative cascade: .theme stays at 1');
is($d->_ast_rule_selector(2), '.override', 'conservative cascade: .override stays at 2');
}
# ââ Conservative: partial conflict blocks merge ââââââââââââââ
{
my $d = Litavis->new;
$d->parse('
.a { color: red; margin: 10px; }
.b { margin: 20px; }
.c { color: red; margin: 10px; }
');
$d->_dedupe(DEDUPE_CONSERVATIVE);
is($d->_ast_rule_count, 3, 'conservative partial conflict: no merge (margin conflict)');
}
# ââ Conservative: no conflict with unrelated props âââââââââââ
{
my $d = Litavis->new;
$d->parse('
.a { color: red; }
.b { font-size: 16px; }
.c { color: red; }
');
$d->_dedupe(DEDUPE_CONSERVATIVE);
is($d->_ast_rule_count, 2, 'conservative no conflict: merged');
is($d->_ast_rule_selector(0), '.a, .c', 'conservative no conflict: selectors joined');
}
# ââ Aggressive: merge despite intervening conflict âââââââââââ
{
my $d = Litavis->new;
$d->parse('
.reset { color: black; }
.theme { color: red; }
.override { color: black; }
');
$d->_dedupe(DEDUPE_AGGRESSIVE);
is($d->_ast_rule_count, 2, 'aggressive: merged despite conflict');
is($d->_ast_rule_selector(0), '.reset, .override', 'aggressive: selectors joined');
is($d->_ast_rule_selector(1), '.theme', 'aggressive: .theme kept');
}
# ââ Dedupe OFF: no changes âââââââââââââââââââââââââââââââââââ
{
my $d = Litavis->new;
$d->parse('
.a { color: red; }
.b { color: red; }
');
$d->_dedupe(DEDUPE_OFF);
is($d->_ast_rule_count, 2, 'dedupe off: no merge');
}
# ââ @-rules are skipped (never merged across) ââââââââââââââââ
{
my $d = Litavis->new;
t/03-cascade.t view on Meta::CPAN
$d->parse('
.a { padding: 8px; }
.b { padding: 8px; }
.c { padding: 8px; }
');
$d->_dedupe(DEDUPE_CONSERVATIVE);
is($d->_ast_rule_count, 1, 'chain merge: all three merged');
like($d->_ast_rule_selector(0), qr/\.a.*\.b.*\.c/, 'chain merge: all selectors present');
}
# ââ Conservative: different prop counts blocks merge âââââââââ
{
my $d = Litavis->new;
$d->parse('
.a { color: red; }
.b { color: red; font-size: 16px; }
');
$d->_dedupe(DEDUPE_CONSERVATIVE);
is($d->_ast_rule_count, 2, 'diff prop count: no merge');
}
# ââ Conservative: same keys different values blocks merge ââââ
{
my $d = Litavis->new;
$d->parse('
.a { color: red; }
.b { color: blue; }
');
$d->_dedupe(DEDUPE_CONSERVATIVE);
is($d->_ast_rule_count, 2, 'diff values: no merge');
}
# ââ Empty rules merge ââââââââââââââââââââââââââââââââââââââââ
{
my $d = Litavis->new;
$d->_ast_add_rule('.a');
$d->_ast_add_rule('.b');
$d->_dedupe(DEDUPE_CONSERVATIVE);
is($d->_ast_rule_count, 1, 'empty rules: merged (both have 0 props)');
}
# ââ Dedupe preserves rule order when no merge ââââââââââââââââ
{
my $d = Litavis->new;
$d->parse('
.first { color: red; }
.second { color: blue; }
.third { color: green; }
');
$d->_dedupe(DEDUPE_CONSERVATIVE);
is($d->_ast_rule_count, 3, 'order preserved: all different');
is($d->_ast_rule_selector(0), '.first', 'order preserved: 0');
is($d->_ast_rule_selector(1), '.second', 'order preserved: 1');
is($d->_ast_rule_selector(2), '.third', 'order preserved: 2');
}
# ââ The cascade problem from the plan ââââââââââââââââââââââââ
{
my $d = Litavis->new;
$d->parse('
.btn { background: grey; color: white; padding: 8px; }
.primary { background: blue; color: white; padding: 8px; }
.btn { background: grey; color: white; padding: 8px; }
');
$d->_dedupe(DEDUPE_CONSERVATIVE);
# Same-selector merge collapses the two .btn into one
# But .btn and .primary share properties, so conservative dedup
# should NOT merge them
is($d->_ast_rule_count, 2, 'cascade problem: .btn merged with itself');
is($d->_ast_rule_selector(0), '.btn', 'cascade problem: .btn first');
is($d->_ast_rule_selector(1), '.primary', 'cascade problem: .primary second');
}
done_testing;
( run in 0.936 second using v1.01-cache-2.11-cpan-df04353d9ac )