Skip to content

Commit ab85483

Browse files
author
Onimock
committed
Fix: incorrect semicolon validation in single-line QSS rule parsing
1 parent 605ade4 commit ab85483

File tree

2 files changed

+151
-26
lines changed

2 files changed

+151
-26
lines changed

src/qss_parser/qss_parser.py

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,7 +1182,7 @@ def _process_complete_rule(
11821182
if any("Duplicate selector" in error for error in errors):
11831183
seen_selectors = set()
11841184
unique_selectors = []
1185-
for sel in state.current_selectors:
1185+
for sel in selectors:
11861186
if sel not in seen_selectors:
11871187
seen_selectors.add(sel)
11881188
unique_selectors.append(sel)
@@ -1197,15 +1197,12 @@ def _process_complete_rule(
11971197
state.current_rules = [QSSRule(sel) for sel in selectors]
11981198
if properties.strip():
11991199
prop_lines = [p.strip() for p in properties.split(";") if p.strip()]
1200-
for i, prop_line in enumerate(prop_lines[:-1]):
1201-
if not prop_line.endswith(";"):
1202-
self._error_handler.dispatch_error(
1203-
f"Error on line {state.current_line}: Property missing ';': {prop_line}"
1204-
)
1200+
for i, prop_line in enumerate(prop_lines):
1201+
if not prop_line:
12051202
continue
12061203
try:
12071204
self._property_processor.process_property(
1208-
prop_line,
1205+
prop_line + ";",
12091206
state.current_rules,
12101207
variable_manager,
12111208
state.current_line,
@@ -1214,25 +1211,6 @@ def _process_complete_rule(
12141211
self._error_handler.dispatch_error(
12151212
f"Error on line {state.current_line}: Invalid property: {prop_line} ({str(e)})"
12161213
)
1217-
last_prop = prop_lines[-1].strip()
1218-
if last_prop:
1219-
parts = last_prop.split(":", 1)
1220-
if len(parts) != 2 or not parts[0].strip() or not parts[1].strip():
1221-
self._error_handler.dispatch_error(
1222-
f"Error on line {state.current_line}: Invalid last property: {last_prop}"
1223-
)
1224-
else:
1225-
try:
1226-
self._property_processor.process_property(
1227-
last_prop + (";" if not last_prop.endswith(";") else ""),
1228-
state.current_rules,
1229-
variable_manager,
1230-
state.current_line,
1231-
)
1232-
except Exception as e:
1233-
self._error_handler.dispatch_error(
1234-
f"Error on line {state.current_line}: Invalid last property: {last_prop} ({str(e)})"
1235-
)
12361214

12371215
for rule in state.current_rules:
12381216
if rule.properties:

tests/test_qss_parser.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,54 @@ def test_parse_invalid_pseudo_state(self) -> None:
491491
)
492492
self.assertEqual(self.parser.to_string(), "", "Should return empty string")
493493

494+
def test_parse_variables_block_single_line(self) -> None:
495+
"""
496+
Test parsing a single-line QSS rule with multiple properties and variables.
497+
"""
498+
qss: str = """
499+
@variables {
500+
--primary-color: #ffffff;
501+
--border-radius: 14px;
502+
}
503+
#extraCloseColumnBtn { background-color: rgba(248, 248, 242, 0); border: none; border-radius: var(--border-radius); color: var(--primary-color); }
504+
"""
505+
self.parser.parse(qss)
506+
self.assertEqual(len(self.parser._state.rules), 1, "Should parse one rule")
507+
rule = self.parser._state.rules[0]
508+
self.assertEqual(rule.selector, "#extraCloseColumnBtn")
509+
self.assertEqual(len(rule.properties), 4, "Should parse all four properties")
510+
self.assertEqual(rule.properties[0].name, "background-color")
511+
self.assertEqual(rule.properties[0].value, "rgba(248, 248, 242, 0)")
512+
self.assertEqual(rule.properties[1].name, "border")
513+
self.assertEqual(rule.properties[1].value, "none")
514+
self.assertEqual(rule.properties[2].name, "border-radius")
515+
self.assertEqual(rule.properties[2].value, "14px")
516+
self.assertEqual(rule.properties[3].name, "color")
517+
self.assertEqual(rule.properties[3].value, "#ffffff")
518+
self.assertEqual(self.errors, [], "Single-line rule should produce no errors")
519+
520+
def test_parse_single_line_multiple_properties(self) -> None:
521+
"""
522+
Test parsing a single-line QSS rule with multiple properties.
523+
"""
524+
qss = """
525+
#extraCloseColumnBtn { background-color: rgba(248, 248, 242, 0); border: none; border-radius: 14px; color: #ffffff; }
526+
"""
527+
self.parser.parse(qss)
528+
self.assertEqual(len(self.parser._state.rules), 1, "Should parse one rule")
529+
rule = self.parser._state.rules[0]
530+
self.assertEqual(rule.selector, "#extraCloseColumnBtn")
531+
self.assertEqual(len(rule.properties), 4, "Should parse all four properties")
532+
self.assertEqual(rule.properties[0].name, "background-color")
533+
self.assertEqual(rule.properties[0].value, "rgba(248, 248, 242, 0)")
534+
self.assertEqual(rule.properties[1].name, "border")
535+
self.assertEqual(rule.properties[1].value, "none")
536+
self.assertEqual(rule.properties[2].name, "border-radius")
537+
self.assertEqual(rule.properties[2].value, "14px")
538+
self.assertEqual(rule.properties[3].name, "color")
539+
self.assertEqual(rule.properties[3].value, "#ffffff")
540+
self.assertEqual(self.errors, [], "Single-line rule should produce no errors")
541+
494542

495543
class TestQSSParserStyleSelection(unittest.TestCase):
496544
def setUp(self) -> None:
@@ -1076,6 +1124,63 @@ def test_get_styles_with_variables_and_fixed_properties(self) -> None:
10761124
errors, [], "Valid variables and properties should produce no errors"
10771125
)
10781126

1127+
def test_get_styles_for_variables_block_single_line(self) -> None:
1128+
"""
1129+
Test get style for a single-line QSS rule with multiple properties and variables.
1130+
"""
1131+
qss: str = """
1132+
@variables {
1133+
--primary-color: #ffffff;
1134+
--border-radius: 14px;
1135+
}
1136+
#extraCloseColumnBtn QPushButton { background-color: rgba(248, 248, 242, 0); border: none; border-radius: var(--border-radius); color: var(--primary-color); }
1137+
"""
1138+
errors: List[str] = []
1139+
self.parser.on(ParserEvent.ERROR_FOUND, lambda error: errors.append(error))
1140+
self.parser.parse(qss)
1141+
widget: Mock = Mock()
1142+
widget.objectName.return_value = "extraCloseColumnBtn"
1143+
widget.metaObject.return_value.className.return_value = "QPushButton"
1144+
stylesheet: str = self.parser.get_styles_for(widget)
1145+
1146+
expected: str = """#extraCloseColumnBtn QPushButton {
1147+
background-color: rgba(248, 248, 242, 0);
1148+
border: none;
1149+
border-radius: 14px;
1150+
color: #ffffff;
1151+
}
1152+
"""
1153+
self.assertEqual(stylesheet.strip(), expected.strip())
1154+
self.assertEqual(
1155+
errors, [], "Valid variables and properties should produce no errors"
1156+
)
1157+
1158+
def test_get_styles_for_single_line_multiple_properties(self) -> None:
1159+
"""
1160+
Test get style for a single-line QSS rule with multiple properties.
1161+
"""
1162+
qss = """
1163+
#extraCloseColumnBtn, QLabel { background-color: rgba(248, 248, 242, 0); border: none; border-radius: 14px; color: #ffffff; }
1164+
"""
1165+
errors: List[str] = []
1166+
self.parser.on(ParserEvent.ERROR_FOUND, lambda error: errors.append(error))
1167+
self.parser.parse(qss)
1168+
widget: Mock = Mock()
1169+
widget.objectName.return_value = "extraCloseColumnBtn"
1170+
widget.metaObject.return_value.className.return_value = "QPushButton"
1171+
stylesheet: str = self.parser.get_styles_for(widget)
1172+
expected: str = """#extraCloseColumnBtn {
1173+
background-color: rgba(248, 248, 242, 0);
1174+
border: none;
1175+
border-radius: 14px;
1176+
color: #ffffff;
1177+
}
1178+
"""
1179+
self.assertEqual(stylesheet.strip(), expected.strip())
1180+
self.assertEqual(
1181+
errors, [], "Valid variables and properties should produce no errors"
1182+
)
1183+
10791184

10801185
class TestQSSParserEvents(unittest.TestCase):
10811186
def setUp(self) -> None:
@@ -1312,6 +1417,48 @@ def test_to_string_complex_nested_selectors_with_attribute_selector_space_invali
13121417
"Invalid attribute selector should produce error",
13131418
)
13141419

1420+
def test_to_string_for_variables_block_single_line(self) -> None:
1421+
"""
1422+
Test to_string a single-line QSS rule with multiple properties and variables.
1423+
"""
1424+
qss: str = """
1425+
@variables {
1426+
--primary-color: #ffffff;
1427+
--border-radius: 14px;
1428+
}
1429+
#extraCloseColumnBtn QPushButton { background-color: rgba(248, 248, 242, 0); border: none; border-radius: var(--border-radius); color: var(--primary-color); }
1430+
"""
1431+
self.parser.parse(qss)
1432+
expected: str = """#extraCloseColumnBtn QPushButton {
1433+
background-color: rgba(248, 248, 242, 0);
1434+
border: none;
1435+
border-radius: 14px;
1436+
color: #ffffff;
1437+
}
1438+
"""
1439+
self.assertEqual(self.parser.to_string(), expected, "Should to string")
1440+
self.assertEqual(self.errors, [], "Single-line rule should produce no errors")
1441+
1442+
def test_to_string_for_single_line_multiple_properties(self) -> None:
1443+
"""
1444+
Test to_string a single-line QSS rule with multiple properties.
1445+
"""
1446+
qss = """
1447+
#extraCloseColumnBtn { background-color: rgba(248, 248, 242, 0); border: none; border-radius: 14px; color: #ffffff; }
1448+
"""
1449+
self.parser.parse(qss)
1450+
expected: str = """#extraCloseColumnBtn {
1451+
background-color: rgba(248, 248, 242, 0);
1452+
border: none;
1453+
border-radius: 14px;
1454+
color: #ffffff;
1455+
}
1456+
"""
1457+
self.assertEqual(self.parser.to_string().strip(), expected.strip())
1458+
self.assertEqual(
1459+
self.errors, [], "Valid variables and properties should produce no errors"
1460+
)
1461+
13151462

13161463
class TestQSSParserQProperty(unittest.TestCase):
13171464
def setUp(self) -> None:

0 commit comments

Comments
 (0)