from pathlib import Path from ui_builder.manager import DEFAULT_HOCKEY_PLAYER_SELECTION_PANELS, UIBuilderManager ROOT = Path(__file__).resolve().parents[1] APP_JS = (ROOT / "ui_builder/static/app.js").read_text(encoding="utf-8") APP_PY = (ROOT / "app.py").read_text(encoding="utf-8") MAPPING = (ROOT / "hockey_data/mapping_context.py").read_text(encoding="utf-8") STYLES = (ROOT / "ui_builder/static/styles.css").read_text(encoding="utf-8") def test_old_hockey_config_gets_default_player_selection_panels(tmp_path: Path): manager = UIBuilderManager(tmp_path) config = { "version": 21, "project_name": "Hockey", "tabs": [{"id": "main", "label": "Игра"}], "components": [{"id": "h", "type": "hockey_penalty_dashboard", "action_id": "hockey_game"}], } normalized = manager._normalize(config) panels = normalized["player_selection_panels"] assert [item["id"] for item in panels] == [item["id"] for item in DEFAULT_HOCKEY_PLAYER_SELECTION_PANELS] assert {item["id"]: item["slots"] for item in panels} == { "compare_home": 2, "compare_away": 2, "compare_mixed": 2, "three_stars": 3, "selected_player": 1, } def test_player_selection_panels_are_persisted_with_quick_panel_settings(): assert 'player_panels = payload.get("player_selection_panels", [])' in APP_PY assert 'draft_config["player_selection_panels"] = player_panels' in APP_PY assert 'published_config["player_selection_panels"]' in APP_PY assert 'player_selection_panels: normalizedPlayerPanels' in APP_JS def test_runtime_player_slots_accept_roster_drag_drop_and_generate_identifiers(): for token in ( "function renderHockeyPlayerSelectionWindows()", 'readHockeyDragData(event, "player")', "hockeySetPlayerSelectionSlot(panel", "player_select.${panelId}.player${slot}_${field}", '"db_id"', '"team_id"', '"side"', ): assert token in APP_JS assert 'panel.rule === "home"' in APP_JS assert 'panel.rule === "away"' in APP_JS assert 'panel.rule === "different_teams"' in APP_JS def test_single_selected_player_panel_mirrors_legacy_signature_context(): assert '"selected_player"' in APP_JS or 'selected_player' in APP_JS assert "sync_selected_player" in APP_JS assert "selected_player_id:" in APP_JS assert "selected_player_team_id:" in APP_JS def test_mapping_catalog_exposes_dynamic_player_selection_groups(): assert 'r"^player_select\\.([A-Za-z0-9_]+)\\.(.+)$"' in MAPPING assert 'Игровые выборки ·' in MAPPING assert "DB ID" in MAPPING assert "ID команды" in MAPPING def test_player_panels_are_collapsible_and_have_editor_ui(): assert "hockey-player-select-panel" in STYLES assert ".hockey-player-select-panel.is-collapsed .hockey-player-select-body" in STYLES assert 'data-player-panel-field="slots"' in APP_JS assert 'data-player-panel-field="rule"' in APP_JS assert 'data-player-panel-add' in APP_JS def test_all_overlay_sequences_latch_onair_state_for_lower_dock(): start = APP_JS.index("function trackRuntimeOverlayCommands") end = APP_JS.index("async function sendRuntimeVmixSequence", start) block = APP_JS[start:end] assert "state.shortcutSequenceOverlayState.set(ownerSequenceId, true);" in block assert "state.shortcutSequenceOverlayState.set(ownerSequenceId, false);" in block assert "refreshQuickPanelOnAirClasses();" in block def test_player_panel_id_edit_rerenders_row_so_followup_fields_use_new_id(): assert 'const eventName = field === "id"' in APP_JS assert 'if (field === "id") {' in APP_JS assert "draftPlayerPanels = normalized;" in APP_JS assert "renderEditor();" in APP_JS