From a36bc75be4679ef24c0b3c148179e65eaf5f693b Mon Sep 17 00:00:00 2001 From: Wolfgang Schoenberger <221313372+wolfiesch@users.noreply.github.com> Date: Tue, 8 Sep 2026 00:52:27 -0700 Subject: [PATCH] fix(adapters): parse wolfxl plain-string fill colors WolfXL exposes PatternFill fgColor as a plain ARGB string where openpyxl returns a Color object, so _color_to_hex returned None for every wolfxl conditional-format rule and the fidelity read lane dropped all format expectations (wolfxl scored 0/3 on conditional_formatting in the 2026-09-08 snapshot). Accept the plain-string spelling alongside Color objects. Verification: wolfxl conditional_formatting now 3/3 read and write against the fixed wolfxl tree; full suite green. --- .../harness/adapters/wolfxl_adapter.py | 7 +++- tests/test_wolfxl_adapter_roundtrip.py | 37 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/excelbench/harness/adapters/wolfxl_adapter.py b/src/excelbench/harness/adapters/wolfxl_adapter.py index c7b9b73..1f6d24f 100644 --- a/src/excelbench/harness/adapters/wolfxl_adapter.py +++ b/src/excelbench/harness/adapters/wolfxl_adapter.py @@ -36,7 +36,12 @@ def _color_to_hex(color: Any) -> str | None: if color is None: return None - rgb = getattr(color, "rgb", None) + if isinstance(color, str): + # WolfXL exposes fill colors as plain ARGB strings where openpyxl + # returns Color objects; both spellings normalize the same way. + rgb: Any = color + else: + rgb = getattr(color, "rgb", None) if isinstance(rgb, str) and len(rgb) >= 6: return f"#{rgb[2:]}" if len(rgb) == 8 else f"#{rgb}" diff --git a/tests/test_wolfxl_adapter_roundtrip.py b/tests/test_wolfxl_adapter_roundtrip.py index 7df91cd..7adb32d 100644 --- a/tests/test_wolfxl_adapter_roundtrip.py +++ b/tests/test_wolfxl_adapter_roundtrip.py @@ -209,3 +209,40 @@ def test_hyperlink_round_trip(adapter: Any, tmp_path: Path) -> None: assert any(link.get("target") == "https://example.com" for link in links) finally: adapter.close_workbook(reopened) + + +def test_conditional_format_read_includes_rule_format( + adapter: Any, tmp_path: Path +) -> None: + from openpyxl import Workbook + from openpyxl.formatting.rule import CellIsRule + from openpyxl.styles import PatternFill + + src = Workbook() + ws = src.active + ws.title = "CF" + for row in range(1, 6): + ws.cell(row=row, column=2, value=row * 10) + ws.conditional_formatting.add( + "B2:B6", + CellIsRule( + operator="greaterThan", + formula=["25"], + fill=PatternFill( + start_color="00FFFF00", end_color="00FFFF00", fill_type="solid" + ), + ), + ) + path = tmp_path / "cf.xlsx" + src.save(path) + + workbook = adapter.open_workbook(path) + try: + rules = adapter.read_conditional_formats(workbook, "CF") + finally: + adapter.close_workbook(workbook) + assert len(rules) == 1 + rule = rules[0] + assert rule["rule_type"] == "cellIs" + assert rule["operator"] == "greaterThan" + assert rule["format"] == {"bg_color": "#FFFF00"}