from __future__ import annotations from hockey_data.agent_bridge import ( _mapping_numeric_value, _mapping_rule_command, _mapping_rule_matches, _mapping_value_function, ) def _item(value: object) -> dict[str, object]: return {"value": value} def test_mapping_numeric_value_supports_stats_formats() -> None: assert _mapping_numeric_value("25") == 25.0 assert _mapping_numeric_value("51%") == 51.0 assert _mapping_numeric_value("08:14") == 494.0 assert _mapping_numeric_value("1:02:03") == 3723.0 assert _mapping_numeric_value("12,5") == 12.5 def test_mapping_rule_matches_numeric_time_and_text() -> None: assert _mapping_rule_matches("25", "gt", "18") is True assert _mapping_rule_matches("08:14", ">", "07:59") is True assert _mapping_rule_matches("49%", "lt", "51%") is True assert _mapping_rule_matches("HOME", "eq", "home") is True assert _mapping_rule_matches("Power Play", "contains", "play") is True assert _mapping_rule_matches("", "empty") is True def test_text_colour_rule_uses_true_and_false_colours() -> None: base = { "enabled": True, "action": "text_colour", "left_key": "stats.home", "operator": "gt", "right_mode": "field", "right_key": "stats.away", "true_value": "#E5CEA8", "false_value": "#FFFFFF", } command, error = _mapping_rule_command( base, field_type="text", input_ref="53", selected_name="HOME_SHOTS.Text", default_data_key="stats.home", by_key={"stats.home": _item(31), "stats.away": _item(25)}, ) assert error == "" assert command == { "Function": "SetTextColour", "Input": "53", "SelectedName": "HOME_SHOTS.Text", "Value": "#E5CEA8", } command, error = _mapping_rule_command( base, field_type="text", input_ref="53", selected_name="HOME_SHOTS.Text", default_data_key="stats.home", by_key={"stats.home": _item(20), "stats.away": _item(25)}, ) assert error == "" assert command is not None assert command["Value"] == "#FFFFFF" def test_visibility_rule_is_deterministic_for_text_and_image() -> None: rule = { "enabled": True, "action": "visibility", "left_key": "stats.home", "operator": "gt", "right_mode": "field", "right_key": "stats.away", "true_value": "on", "false_value": "off", } text_command, text_error = _mapping_rule_command( rule, field_type="text", input_ref="53", selected_name="HOME_ARROW.Text", default_data_key="stats.home", by_key={"stats.home": _item(10), "stats.away": _item(7)}, ) assert text_error == "" assert text_command is not None assert text_command["Function"] == "SetTextVisibleOn" image_command, image_error = _mapping_rule_command( rule, field_type="image", input_ref="53", selected_name="HOME_ARROW.Source", default_data_key="image.path", by_key={"stats.home": _item(5), "stats.away": _item(7)}, ) assert image_error == "" assert image_command is not None assert image_command["Function"] == "SetImageVisibleOff" assert "Value" not in image_command def test_rule_can_compare_constant_and_reports_missing_source() -> None: rule = { "enabled": True, "action": "visibility", "left_key": "penalties", "operator": "gte", "right_mode": "value", "right_value": "10", "true_value": "on", "false_value": "off", } command, error = _mapping_rule_command( rule, field_type="text", input_ref="1", selected_name="WARNING.Text", default_data_key="penalties", by_key={"penalties": _item(12)}, ) assert error == "" assert command is not None assert command["Function"] == "SetTextVisibleOn" missing, error = _mapping_rule_command( {**rule, "left_key": "missing"}, field_type="text", input_ref="1", selected_name="WARNING.Text", default_data_key="penalties", by_key={"penalties": _item(12)}, ) assert missing is None assert error == "missing_left:missing" def test_mapping_value_function_respects_mapping_type() -> None: assert _mapping_value_function("text") == "SetText" assert _mapping_value_function("image") == "SetImage" assert _mapping_value_function("source") == "SetImage" assert _mapping_value_function("color") == "SetColor" def test_mapping_rule_controls_do_not_trigger_card_rerender() -> None: from pathlib import Path source = (Path(__file__).resolve().parents[1] / "hockey_data" / "static" / "admin-directories.js").read_text(encoding="utf-8") assert 'event.target.closest("button, input, select, textarea, label, a' in source assert '.hockey-map-rule-wrap' in source def test_mapping_vmix_scroll_is_preserved_across_save_and_rerender() -> None: from pathlib import Path source = (Path(__file__).resolve().parents[1] / "hockey_data" / "static" / "admin-directories.js").read_text(encoding="utf-8") assert 'mappingVmixScrollTop: 0' in source assert 'vmixScroll.scrollTop = Math.max(0, Number(state.mappingVmixScrollTop || 0))' in source assert 'state.mappingVmixScrollTop = vmixScroll.scrollTop' in source assert 'if (currentScroll) state.mappingVmixScrollTop = currentScroll.scrollTop' in source