Added Rules preset (on top of format and style presets)

This commit is contained in:
2026-03-13 21:02:03 +01:00
parent 3105b72ac2
commit 3d1a391cba
7 changed files with 848 additions and 131 deletions

View File

@@ -0,0 +1,167 @@
"""Tests for DEFAULT_RULE_PRESETS expansion in FormattingEngine."""
import pytest
from myfasthtml.core.formatting.dataclasses import FormatRule, NumberFormatter, RulePreset
from myfasthtml.core.formatting.engine import FormattingEngine
from myfasthtml.core.formatting.style_resolver import StyleContainer
# ============================================================
# Helpers
# ============================================================
def make_preset_rule(preset_name: str) -> list[FormatRule]:
"""Simulate what format("preset_name") produces from the DSL transformer."""
return [FormatRule(formatter=NumberFormatter(preset=preset_name))]
# ============================================================
# accounting preset
# ============================================================
def test_i_can_use_accounting_preset_with_negative_value():
"""Negative value → parentheses format, no style."""
engine = FormattingEngine()
rules = make_preset_rule("accounting")
css, formatted = engine.apply_format(rules, cell_value=-12500)
assert formatted == "(12 500)"
assert css is None
def test_i_can_use_accounting_preset_with_positive_value():
"""Positive value → plain number format, no style."""
engine = FormattingEngine()
rules = make_preset_rule("accounting")
css, formatted = engine.apply_format(rules, cell_value=8340)
assert formatted == "8 340"
assert css is None
def test_i_can_use_accounting_preset_with_zero():
"""Zero does not match < 0 or > 0 → no formatter, no style."""
engine = FormattingEngine()
rules = make_preset_rule("accounting")
css, formatted = engine.apply_format(rules, cell_value=0)
assert formatted is None
assert css is None
# ============================================================
# traffic_light preset
# ============================================================
def test_i_can_use_traffic_light_preset_with_negative_value():
"""Negative value → error style (red)."""
engine = FormattingEngine()
rules = make_preset_rule("traffic_light")
css, formatted = engine.apply_format(rules, cell_value=-1)
assert css is not None
assert css.cls == "mf-formatting-error"
assert formatted is None
def test_i_can_use_traffic_light_preset_with_zero():
"""Zero → warning style (yellow)."""
engine = FormattingEngine()
rules = make_preset_rule("traffic_light")
css, formatted = engine.apply_format(rules, cell_value=0)
assert css is not None
assert css.cls == "mf-formatting-warning"
def test_i_can_use_traffic_light_preset_with_positive_value():
"""Positive value → success style (green)."""
engine = FormattingEngine()
rules = make_preset_rule("traffic_light")
css, formatted = engine.apply_format(rules, cell_value=42)
assert css is not None
assert css.cls == "mf-formatting-success"
# ============================================================
# budget_variance preset
# ============================================================
def test_i_can_use_budget_variance_preset_with_negative_value():
"""Negative variance → percentage format + error style."""
engine = FormattingEngine()
rules = make_preset_rule("budget_variance")
css, formatted = engine.apply_format(rules, cell_value=-0.08)
assert formatted == "-8.0%"
assert css is not None
assert css.cls == "mf-formatting-error"
def test_i_can_use_budget_variance_preset_over_threshold():
"""Variance > 10% → percentage format + warning style."""
engine = FormattingEngine()
rules = make_preset_rule("budget_variance")
css, formatted = engine.apply_format(rules, cell_value=0.15)
assert formatted == "15.0%"
assert css is not None
assert css.cls == "mf-formatting-warning"
def test_i_can_use_budget_variance_preset_within_range():
"""Variance within 010% → percentage format, no special style."""
engine = FormattingEngine()
rules = make_preset_rule("budget_variance")
css, formatted = engine.apply_format(rules, cell_value=0.05)
assert formatted == "5.0%"
assert css is None
# ============================================================
# Unknown preset → no expansion, engine falls back gracefully
# ============================================================
def test_i_cannot_use_unknown_rule_preset():
"""Unknown preset name is not expanded — treated as plain formatter preset (no crash)."""
engine = FormattingEngine()
rules = make_preset_rule("nonexistent_preset")
# Should not raise; returns defaults (no special formatting)
css, formatted = engine.apply_format(rules, cell_value=42)
assert css is None
# ============================================================
# Custom rule_presets override
# ============================================================
def test_i_can_pass_custom_rule_presets():
"""Engine accepts custom rule_presets dict of RulePreset, overriding defaults."""
custom = {
"my_preset": RulePreset(
name="my_preset",
description="Custom test preset",
rules=[
FormatRule(formatter=NumberFormatter(precision=2, suffix=" pts")),
],
)
}
engine = FormattingEngine(rule_presets=custom)
rules = make_preset_rule("my_preset")
_, formatted = engine.apply_format(rules, cell_value=9.5)
assert formatted == "9.50 pts"