I can validate formatting in editor

This commit is contained in:
2026-02-01 21:49:46 +01:00
parent d7ec99c3d9
commit 0620cb678b
23 changed files with 794 additions and 501 deletions

View File

@@ -5,23 +5,19 @@ Tests the parsing of DSL text into ScopedRule objects.
"""
import pytest
from myfasthtml.core.formatting.dsl import (
parse_dsl,
ColumnScope,
RowScope,
CellScope,
ScopedRule,
DSLSyntaxError,
)
from myfasthtml.core.formatting.dataclasses import (
Condition,
Style,
FormatRule,
NumberFormatter,
DateFormatter,
BooleanFormatter,
TextFormatter,
EnumFormatter,
NumberFormatter,
DateFormatter,
BooleanFormatter,
TextFormatter,
EnumFormatter,
)
from myfasthtml.core.formatting.dsl import (
parse_dsl,
ColumnScope,
RowScope,
CellScope,
DSLSyntaxError,
)
@@ -31,102 +27,102 @@ from myfasthtml.core.formatting.dataclasses import (
class TestColumnScope:
"""Tests for column scope parsing."""
def test_i_can_parse_column_scope(self):
"""Test parsing a simple column scope."""
dsl = """
"""Tests for column scope parsing."""
def test_i_can_parse_column_scope(self):
"""Test parsing a simple column scope."""
dsl = """
column amount:
style("error")
"""
rules = parse_dsl(dsl)
assert len(rules) == 1
assert isinstance(rules[0].scope, ColumnScope)
assert rules[0].scope.column == "amount"
def test_i_can_parse_column_scope_with_quoted_name(self):
"""Test parsing a column scope with quoted name containing spaces."""
dsl = """
rules = parse_dsl(dsl)
assert len(rules) == 1
assert isinstance(rules[0].scope, ColumnScope)
assert rules[0].scope.column == "amount"
def test_i_can_parse_column_scope_with_quoted_name(self):
"""Test parsing a column scope with quoted name containing spaces."""
dsl = """
column "total amount":
style("error")
"""
rules = parse_dsl(dsl)
assert len(rules) == 1
assert isinstance(rules[0].scope, ColumnScope)
assert rules[0].scope.column == "total amount"
rules = parse_dsl(dsl)
assert len(rules) == 1
assert isinstance(rules[0].scope, ColumnScope)
assert rules[0].scope.column == "total amount"
class TestRowScope:
"""Tests for row scope parsing."""
def test_i_can_parse_row_scope(self):
"""Test parsing a row scope."""
dsl = """
"""Tests for row scope parsing."""
def test_i_can_parse_row_scope(self):
"""Test parsing a row scope."""
dsl = """
row 0:
style("neutral")
"""
rules = parse_dsl(dsl)
assert len(rules) == 1
assert isinstance(rules[0].scope, RowScope)
assert rules[0].scope.row == 0
def test_i_can_parse_row_scope_with_large_index(self):
"""Test parsing a row scope with a large index."""
dsl = """
rules = parse_dsl(dsl)
assert len(rules) == 1
assert isinstance(rules[0].scope, RowScope)
assert rules[0].scope.row == 0
def test_i_can_parse_row_scope_with_large_index(self):
"""Test parsing a row scope with a large index."""
dsl = """
row 999:
style("highlight")
"""
rules = parse_dsl(dsl)
assert len(rules) == 1
assert rules[0].scope.row == 999
rules = parse_dsl(dsl)
assert len(rules) == 1
assert rules[0].scope.row == 999
class TestCellScope:
"""Tests for cell scope parsing."""
def test_i_can_parse_cell_scope_with_coords(self):
"""Test parsing a cell scope with coordinates."""
dsl = """
"""Tests for cell scope parsing."""
def test_i_can_parse_cell_scope_with_coords(self):
"""Test parsing a cell scope with coordinates."""
dsl = """
cell (amount, 3):
style("highlight")
"""
rules = parse_dsl(dsl)
assert len(rules) == 1
assert isinstance(rules[0].scope, CellScope)
assert rules[0].scope.column == "amount"
assert rules[0].scope.row == 3
assert rules[0].scope.cell_id is None
def test_i_can_parse_cell_scope_with_quoted_column(self):
"""Test parsing a cell scope with quoted column name."""
dsl = """
rules = parse_dsl(dsl)
assert len(rules) == 1
assert isinstance(rules[0].scope, CellScope)
assert rules[0].scope.column == "amount"
assert rules[0].scope.row == 3
assert rules[0].scope.cell_id is None
def test_i_can_parse_cell_scope_with_quoted_column(self):
"""Test parsing a cell scope with quoted column name."""
dsl = """
cell ("total amount", 5):
style("highlight")
"""
rules = parse_dsl(dsl)
assert len(rules) == 1
assert rules[0].scope.column == "total amount"
assert rules[0].scope.row == 5
def test_i_can_parse_cell_scope_with_id(self):
"""Test parsing a cell scope with cell ID."""
dsl = """
rules = parse_dsl(dsl)
assert len(rules) == 1
assert rules[0].scope.column == "total amount"
assert rules[0].scope.row == 5
def test_i_can_parse_cell_scope_with_id(self):
"""Test parsing a cell scope with cell ID."""
dsl = """
cell tcell_grid1-3-2:
style("highlight")
"""
rules = parse_dsl(dsl)
assert len(rules) == 1
assert isinstance(rules[0].scope, CellScope)
assert rules[0].scope.cell_id == "tcell_grid1-3-2"
assert rules[0].scope.column is None
assert rules[0].scope.row is None
rules = parse_dsl(dsl)
assert len(rules) == 1
assert isinstance(rules[0].scope, CellScope)
assert rules[0].scope.cell_id == "tcell_grid1-3-2"
assert rules[0].scope.column is None
assert rules[0].scope.row is None
# =============================================================================
@@ -135,61 +131,61 @@ cell tcell_grid1-3-2:
class TestStyleParsing:
"""Tests for style expression parsing."""
def test_i_can_parse_style_with_preset(self):
"""Test parsing style with preset only."""
dsl = """
"""Tests for style expression parsing."""
def test_i_can_parse_style_with_preset(self):
"""Test parsing style with preset only."""
dsl = """
column amount:
style("error")
"""
rules = parse_dsl(dsl)
assert rules[0].rule.style is not None
assert rules[0].rule.style.preset == "error"
def test_i_can_parse_style_without_preset(self):
"""Test parsing style without preset, with direct properties."""
dsl = """
rules = parse_dsl(dsl)
assert rules[0].rule.style is not None
assert rules[0].rule.style.preset == "error"
def test_i_can_parse_style_without_preset(self):
"""Test parsing style without preset, with direct properties."""
dsl = """
column amount:
style(color="red", bold=True)
"""
rules = parse_dsl(dsl)
style = rules[0].rule.style
assert style.preset is None
assert style.color == "red"
assert style.font_weight == "bold"
def test_i_can_parse_style_with_preset_and_options(self):
"""Test parsing style with preset and additional options."""
dsl = """
rules = parse_dsl(dsl)
style = rules[0].rule.style
assert style.preset is None
assert style.color == "red"
assert style.font_weight == "bold"
def test_i_can_parse_style_with_preset_and_options(self):
"""Test parsing style with preset and additional options."""
dsl = """
column amount:
style("error", bold=True, italic=True)
"""
rules = parse_dsl(dsl)
style = rules[0].rule.style
assert style.preset == "error"
assert style.font_weight == "bold"
assert style.font_style == "italic"
@pytest.mark.parametrize("option,attr_name,attr_value", [
("bold=True", "font_weight", "bold"),
("italic=True", "font_style", "italic"),
("underline=True", "text_decoration", "underline"),
("strikethrough=True", "text_decoration", "line-through"),
])
def test_i_can_parse_style_options(self, option, attr_name, attr_value):
"""Test parsing individual style options."""
dsl = f"""
rules = parse_dsl(dsl)
style = rules[0].rule.style
assert style.preset == "error"
assert style.font_weight == "bold"
assert style.font_style == "italic"
@pytest.mark.parametrize("option,attr_name,attr_value", [
("bold=True", "font_weight", "bold"),
("italic=True", "font_style", "italic"),
("underline=True", "text_decoration", "underline"),
("strikethrough=True", "text_decoration", "line-through"),
])
def test_i_can_parse_style_options(self, option, attr_name, attr_value):
"""Test parsing individual style options."""
dsl = f"""
column amount:
style({option})
"""
rules = parse_dsl(dsl)
style = rules[0].rule.style
assert getattr(style, attr_name) == attr_value
rules = parse_dsl(dsl)
style = rules[0].rule.style
assert getattr(style, attr_name) == attr_value
# =============================================================================
@@ -198,100 +194,100 @@ column amount:
class TestFormatParsing:
"""Tests for format expression parsing."""
def test_i_can_parse_format_preset(self):
"""Test parsing format with preset."""
dsl = """
"""Tests for format expression parsing."""
def test_i_can_parse_format_preset(self):
"""Test parsing format with preset."""
dsl = """
column amount:
format("EUR")
"""
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert formatter is not None
assert formatter.preset == "EUR"
def test_i_can_parse_format_preset_with_options(self):
"""Test parsing format preset with options."""
dsl = """
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert formatter is not None
assert formatter.preset == "EUR"
def test_i_can_parse_format_preset_with_options(self):
"""Test parsing format preset with options."""
dsl = """
column amount:
format("EUR", precision=3)
"""
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert formatter.preset == "EUR"
assert formatter.precision == 3
@pytest.mark.parametrize("format_type,formatter_class", [
("number", NumberFormatter),
("date", DateFormatter),
("boolean", BooleanFormatter),
("text", TextFormatter),
("enum", EnumFormatter),
])
def test_i_can_parse_format_types(self, format_type, formatter_class):
"""Test parsing explicit format types."""
dsl = f"""
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert formatter.preset == "EUR"
assert formatter.precision == 3
@pytest.mark.parametrize("format_type,formatter_class", [
("number", NumberFormatter),
("date", DateFormatter),
("boolean", BooleanFormatter),
("text", TextFormatter),
("enum", EnumFormatter),
])
def test_i_can_parse_format_types(self, format_type, formatter_class):
"""Test parsing explicit format types."""
dsl = f"""
column amount:
format.{format_type}()
"""
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert isinstance(formatter, formatter_class)
def test_i_can_parse_format_number_with_options(self):
"""Test parsing format.number with all options."""
dsl = """
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert isinstance(formatter, formatter_class)
def test_i_can_parse_format_number_with_options(self):
"""Test parsing format.number with all options."""
dsl = """
column amount:
format.number(precision=2, suffix=" EUR", thousands_sep=" ")
"""
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert isinstance(formatter, NumberFormatter)
assert formatter.precision == 2
assert formatter.suffix == " EUR"
assert formatter.thousands_sep == " "
def test_i_can_parse_format_date_with_options(self):
"""Test parsing format.date with format option."""
dsl = """
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert isinstance(formatter, NumberFormatter)
assert formatter.precision == 2
assert formatter.suffix == " EUR"
assert formatter.thousands_sep == " "
def test_i_can_parse_format_date_with_options(self):
"""Test parsing format.date with format option."""
dsl = """
column created_at:
format.date(format="%d/%m/%Y")
"""
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert isinstance(formatter, DateFormatter)
assert formatter.format == "%d/%m/%Y"
def test_i_can_parse_format_boolean_with_options(self):
"""Test parsing format.boolean with all options."""
dsl = """
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert isinstance(formatter, DateFormatter)
assert formatter.format == "%d/%m/%Y"
def test_i_can_parse_format_boolean_with_options(self):
"""Test parsing format.boolean with all options."""
dsl = """
column active:
format.boolean(true_value="Oui", false_value="Non")
"""
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert isinstance(formatter, BooleanFormatter)
assert formatter.true_value == "Oui"
assert formatter.false_value == "Non"
def test_i_can_parse_format_enum_with_source(self):
"""Test parsing format.enum with source mapping."""
dsl = """
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert isinstance(formatter, BooleanFormatter)
assert formatter.true_value == "Oui"
assert formatter.false_value == "Non"
def test_i_can_parse_format_enum_with_source(self):
"""Test parsing format.enum with source mapping."""
dsl = """
column status:
format.enum(source={"draft": "Brouillon", "published": "Publie"})
"""
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert isinstance(formatter, EnumFormatter)
assert formatter.source == {"draft": "Brouillon", "published": "Publie"}
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert isinstance(formatter, EnumFormatter)
assert formatter.source == {"draft": "Brouillon", "published": "Publie"}
# =============================================================================
@@ -300,89 +296,89 @@ column status:
class TestConditionParsing:
"""Tests for condition parsing."""
@pytest.mark.parametrize("operator,dsl_op", [
("==", "=="),
("!=", "!="),
("<", "<"),
("<=", "<="),
(">", ">"),
(">=", ">="),
("contains", "contains"),
("startswith", "startswith"),
("endswith", "endswith"),
])
def test_i_can_parse_comparison_operators(self, operator, dsl_op):
"""Test parsing all comparison operators."""
dsl = f"""
"""Tests for condition parsing."""
@pytest.mark.parametrize("operator,dsl_op", [
("==", "=="),
("!=", "!="),
("<", "<"),
("<=", "<="),
(">", ">"),
(">=", ">="),
("contains", "contains"),
("startswith", "startswith"),
("endswith", "endswith"),
])
def test_i_can_parse_comparison_operators(self, operator, dsl_op):
"""Test parsing all comparison operators."""
dsl = f"""
column amount:
style("error") if value {dsl_op} 0
"""
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition is not None
assert condition.operator == operator
@pytest.mark.parametrize("unary_op", ["isempty", "isnotempty"])
def test_i_can_parse_unary_conditions(self, unary_op):
"""Test parsing unary conditions (isempty, isnotempty)."""
dsl = f"""
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition is not None
assert condition.operator == operator
@pytest.mark.parametrize("unary_op", ["isempty", "isnotempty"])
def test_i_can_parse_unary_conditions(self, unary_op):
"""Test parsing unary conditions (isempty, isnotempty)."""
dsl = f"""
column name:
style("neutral") if value {unary_op}
"""
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.operator == unary_op
def test_i_can_parse_condition_in(self):
"""Test parsing 'in' condition with list."""
dsl = """
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.operator == unary_op
def test_i_can_parse_condition_in(self):
"""Test parsing 'in' condition with list."""
dsl = """
column status:
style("success") if value in ["approved", "validated"]
"""
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.operator == "in"
assert condition.value == ["approved", "validated"]
def test_i_can_parse_condition_between(self):
"""Test parsing 'between' condition."""
dsl = """
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.operator == "in"
assert condition.value == ["approved", "validated"]
def test_i_can_parse_condition_between(self):
"""Test parsing 'between' condition."""
dsl = """
column score:
style("warning") if value between 30 and 70
"""
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.operator == "between"
assert condition.value == [30, 70]
def test_i_can_parse_condition_negation(self):
"""Test parsing negated condition."""
dsl = """
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.operator == "between"
assert condition.value == [30, 70]
def test_i_can_parse_condition_negation(self):
"""Test parsing negated condition."""
dsl = """
column status:
style("error") if not value == "approved"
"""
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.negate is True
assert condition.operator == "=="
def test_i_can_parse_condition_case_sensitive(self):
"""Test parsing case-sensitive condition."""
dsl = """
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.negate is True
assert condition.operator == "=="
def test_i_can_parse_condition_case_sensitive(self):
"""Test parsing case-sensitive condition."""
dsl = """
column name:
style("error") if value == "Error" (case)
"""
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.case_sensitive is True
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.case_sensitive is True
# =============================================================================
@@ -391,31 +387,31 @@ column name:
class TestLiteralParsing:
"""Tests for literal value parsing in conditions."""
@pytest.mark.parametrize("literal,expected_value,expected_type", [
('"hello"', "hello", str),
("'world'", "world", str),
("42", 42, int),
("-10", -10, int),
("3.14", 3.14, float),
("-2.5", -2.5, float),
("True", True, bool),
("False", False, bool),
("true", True, bool),
("false", False, bool),
])
def test_i_can_parse_literals(self, literal, expected_value, expected_type):
"""Test parsing various literal types in conditions."""
dsl = f"""
"""Tests for literal value parsing in conditions."""
@pytest.mark.parametrize("literal,expected_value,expected_type", [
('"hello"', "hello", str),
("'world'", "world", str),
("42", 42, int),
("-10", -10, int),
("3.14", 3.14, float),
("-2.5", -2.5, float),
("True", True, bool),
("False", False, bool),
("true", True, bool),
("false", False, bool),
])
def test_i_can_parse_literals(self, literal, expected_value, expected_type):
"""Test parsing various literal types in conditions."""
dsl = f"""
column amount:
style("error") if value == {literal}
"""
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.value == expected_value
assert isinstance(condition.value, expected_type)
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.value == expected_value
assert isinstance(condition.value, expected_type)
# =============================================================================
@@ -424,29 +420,29 @@ column amount:
class TestReferenceParsing:
"""Tests for cell reference parsing in conditions."""
def test_i_can_parse_column_reference(self):
"""Test parsing column reference in condition."""
dsl = """
"""Tests for cell reference parsing in conditions."""
def test_i_can_parse_column_reference(self):
"""Test parsing column reference in condition."""
dsl = """
column actual:
style("error") if value > col.budget
"""
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.value == {"col": "budget"}
def test_i_can_parse_column_reference_with_quoted_name(self):
"""Test parsing column reference with quoted name."""
dsl = """
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.value == {"col": "budget"}
def test_i_can_parse_column_reference_with_quoted_name(self):
"""Test parsing column reference with quoted name."""
dsl = """
column actual:
style("error") if value > col."max budget"
"""
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.value == {"col": "max budget"}
rules = parse_dsl(dsl)
condition = rules[0].rule.condition
assert condition.value == {"col": "max budget"}
# =============================================================================
@@ -455,27 +451,27 @@ column actual:
class TestComplexStructures:
"""Tests for complex DSL structures."""
def test_i_can_parse_multiple_rules_in_scope(self):
"""Test parsing multiple rules under one scope."""
dsl = """
"""Tests for complex DSL structures."""
def test_i_can_parse_multiple_rules_in_scope(self):
"""Test parsing multiple rules under one scope."""
dsl = """
column amount:
style("error") if value < 0
style("success") if value > 1000
format("EUR")
"""
rules = parse_dsl(dsl)
assert len(rules) == 3
# All rules share the same scope
for rule in rules:
assert isinstance(rule.scope, ColumnScope)
assert rule.scope.column == "amount"
def test_i_can_parse_multiple_scopes(self):
"""Test parsing multiple scopes."""
dsl = """
rules = parse_dsl(dsl)
assert len(rules) == 3
# All rules share the same scope
for rule in rules:
assert isinstance(rule.scope, ColumnScope)
assert rule.scope.column == "amount"
def test_i_can_parse_multiple_scopes(self):
"""Test parsing multiple scopes."""
dsl = """
column amount:
format("EUR")
@@ -485,51 +481,51 @@ column status:
row 0:
style("neutral", bold=True)
"""
rules = parse_dsl(dsl)
assert len(rules) == 3
# First rule: column amount
assert isinstance(rules[0].scope, ColumnScope)
assert rules[0].scope.column == "amount"
# Second rule: column status
assert isinstance(rules[1].scope, ColumnScope)
assert rules[1].scope.column == "status"
# Third rule: row 0
assert isinstance(rules[2].scope, RowScope)
assert rules[2].scope.row == 0
def test_i_can_parse_style_and_format_combined(self):
"""Test parsing style and format on same line."""
dsl = """
rules = parse_dsl(dsl)
assert len(rules) == 3
# First rule: column amount
assert isinstance(rules[0].scope, ColumnScope)
assert rules[0].scope.column == "amount"
# Second rule: column status
assert isinstance(rules[1].scope, ColumnScope)
assert rules[1].scope.column == "status"
# Third rule: row 0
assert isinstance(rules[2].scope, RowScope)
assert rules[2].scope.row == 0
def test_i_can_parse_style_and_format_combined(self):
"""Test parsing style and format on same line."""
dsl = """
column amount:
style("error") format("EUR") if value < 0
"""
rules = parse_dsl(dsl)
assert len(rules) == 1
rule = rules[0].rule
assert rule.style is not None
assert rule.style.preset == "error"
assert rule.formatter is not None
assert rule.formatter.preset == "EUR"
assert rule.condition is not None
assert rule.condition.operator == "<"
def test_i_can_parse_comments(self):
"""Test that comments are ignored."""
dsl = """
rules = parse_dsl(dsl)
assert len(rules) == 1
rule = rules[0].rule
assert rule.style is not None
assert rule.style.preset == "error"
assert rule.formatter is not None
assert rule.formatter.preset == "EUR"
assert rule.condition is not None
assert rule.condition.operator == "<"
def test_i_can_parse_comments(self):
"""Test that comments are ignored."""
dsl = """
# This is a comment
column amount:
# Another comment
style("error") if value < 0
"""
rules = parse_dsl(dsl)
assert len(rules) == 1
assert rules[0].rule.style.preset == "error"
rules = parse_dsl(dsl)
assert len(rules) == 1
assert rules[0].rule.style.preset == "error"
# =============================================================================
@@ -538,39 +534,39 @@ column amount:
class TestSyntaxErrors:
"""Tests for syntax error handling."""
def test_i_cannot_parse_invalid_syntax(self):
"""Test that invalid syntax raises DSLSyntaxError."""
dsl = """
"""Tests for syntax error handling."""
def test_i_cannot_parse_invalid_syntax(self):
"""Test that invalid syntax raises DSLSyntaxError."""
dsl = """
column amount
style("error")
"""
with pytest.raises(DSLSyntaxError):
parse_dsl(dsl)
def test_i_cannot_parse_missing_indent(self):
"""Test that missing indentation raises DSLSyntaxError."""
dsl = """
with pytest.raises(DSLSyntaxError):
parse_dsl(dsl)
def test_i_cannot_parse_missing_indent(self):
"""Test that missing indentation raises DSLSyntaxError."""
dsl = """
column amount:
style("error")
"""
with pytest.raises(DSLSyntaxError):
parse_dsl(dsl)
def test_i_cannot_parse_empty_scope(self):
"""Test that empty scope raises DSLSyntaxError."""
dsl = """
with pytest.raises(DSLSyntaxError):
parse_dsl(dsl)
def test_i_cannot_parse_empty_scope(self):
"""Test that empty scope raises DSLSyntaxError."""
dsl = """
column amount:
"""
with pytest.raises(DSLSyntaxError):
parse_dsl(dsl)
def test_i_cannot_parse_invalid_operator(self):
"""Test that invalid operator raises DSLSyntaxError."""
dsl = """
with pytest.raises(DSLSyntaxError):
parse_dsl(dsl)
def test_i_cannot_parse_invalid_operator(self):
"""Test that invalid operator raises DSLSyntaxError."""
dsl = """
column amount:
style("error") if value <> 0
"""
with pytest.raises(DSLSyntaxError):
parse_dsl(dsl)
with pytest.raises(DSLSyntaxError):
parse_dsl(dsl)