mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-02 17:28:05 +00:00
Help users quickly find settings and understand how each one works. Tooltips: a new delegated controller (static/v3/js/tooltips.js) drives an accessible (i) info tooltip that appears on hover, keyboard focus, and tap. A shared `help_tip` Jinja macro (partials/_macros.html) emits the trigger; the plugin config macro and the core settings partials now surface help text through it. Per the design, the always-visible field help paragraphs are folded into the tooltip to declutter the forms, and the hardware/display settings carry authored detail (default, range, recommendation). Search: a global header search box finds settings across every settings tab — even ones not yet opened — via a lazy client-side index built by scanning the same field markup (static/v3/js/settings-search.js). Selecting a result switches tabs, waits for the field to load, then scrolls to and flashes it. A per-tab filter box hides non-matching fields on the current tab. Plugin settings get tooltips for free by reusing each field's schema `description`; every settings field also gets a stable `setting-<tab>-<key>` anchor id for search navigation. Styling uses the existing --color-* theme vars so light/dark mode both work, and honors prefers-reduced-motion. Adds Flask render smoke tests that assert each settings partial ships tooltips, anchors, and a filter box. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gZxznuxw8L92FUMBN3Nqz
118 lines
4.5 KiB
Python
118 lines
4.5 KiB
Python
"""
|
|
Smoke tests for the settings tooltips + search UI.
|
|
|
|
These render the settings partials through Flask and assert that every settings
|
|
field carries:
|
|
- a stable search anchor id (`id="setting-..."` on its .form-group), and
|
|
- an info tooltip (`class="help-tip"` emitted by the help_tip macro).
|
|
|
|
They guard against macro/import breakage and against fields losing their anchor
|
|
or tooltip when partials are edited. See web_interface/static/v3/js/tooltips.js
|
|
and settings-search.js for the consumers of this markup.
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
from flask import Flask
|
|
|
|
PROJECT_ROOT = Path(__file__).parent.parent
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
|
|
# A realistic-enough config so the hand-written partials render every field.
|
|
REALISTIC_CONFIG = {
|
|
"web_display_autostart": True,
|
|
"timezone": "America/Chicago",
|
|
"location": {"city": "Dallas", "state": "Texas", "country": "US"},
|
|
"plugin_system": {
|
|
"auto_discover": True,
|
|
"auto_load_enabled": True,
|
|
"development_mode": False,
|
|
"plugins_directory": "plugin-repos",
|
|
},
|
|
"schedule": {},
|
|
"dim_schedule": {"dim_brightness": 30},
|
|
"sync": {"role": "standalone", "port": 5765, "follower_position": "left"},
|
|
"display": {
|
|
"hardware": {
|
|
"rows": 32, "cols": 64, "chain_length": 2, "parallel": 1,
|
|
"brightness": 95, "hardware_mapping": "adafruit-hat-pwm",
|
|
"led_rgb_sequence": "RGB", "multiplexing": 0, "panel_type": "",
|
|
"row_address_type": 0, "scan_mode": 0, "pwm_bits": 9,
|
|
"pwm_dither_bits": 1, "pwm_lsb_nanoseconds": 130,
|
|
"limit_refresh_rate_hz": 120, "disable_hardware_pulsing": False,
|
|
"inverse_colors": False, "show_refresh_rate": False,
|
|
},
|
|
"runtime": {"gpio_slowdown": 3, "rp1_rio": 0},
|
|
"double_sided": {"enabled": False, "copies": 2, "axis": "horizontal"},
|
|
"use_short_date_format": False,
|
|
"dynamic_duration": {"max_duration_seconds": 180},
|
|
"vegas_scroll": {
|
|
"enabled": False, "scroll_speed": 50, "separator_width": 32,
|
|
"target_fps": 125, "buffer_ahead": 2,
|
|
"plugin_order": [], "excluded_plugins": [],
|
|
},
|
|
"display_durations": {"clock": 15, "weather": 30},
|
|
},
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
base = PROJECT_ROOT / "web_interface"
|
|
app = Flask(
|
|
__name__,
|
|
template_folder=str(base / "templates"),
|
|
static_folder=str(base / "static"),
|
|
)
|
|
app.config["TESTING"] = True
|
|
|
|
from web_interface.blueprints import pages_v3 as pv
|
|
|
|
mock_cm = MagicMock()
|
|
mock_cm.load_config.return_value = REALISTIC_CONFIG
|
|
mock_cm.get_raw_file_content.return_value = REALISTIC_CONFIG
|
|
mock_cm.get_config_path.return_value = "config/config.json"
|
|
mock_cm.get_secrets_path.return_value = "config/config_secrets.json"
|
|
pv.pages_v3.config_manager = mock_cm
|
|
pv.pages_v3.plugin_manager = MagicMock(plugins={})
|
|
|
|
app.register_blueprint(pv.pages_v3, url_prefix="/v3")
|
|
return app.test_client()
|
|
|
|
|
|
# Settings tabs that must expose searchable, tooltipped fields.
|
|
SETTINGS_TABS = ["general", "display", "durations", "schedule", "wifi"]
|
|
|
|
|
|
@pytest.mark.parametrize("tab", SETTINGS_TABS)
|
|
def test_settings_partial_has_tooltips_and_anchors(client, tab):
|
|
resp = client.get(f"/v3/partials/{tab}")
|
|
assert resp.status_code == 200, f"{tab} partial failed to render"
|
|
body = resp.get_data(as_text=True)
|
|
|
|
assert 'class="help-tip"' in body, f"{tab}: no tooltips rendered"
|
|
assert 'id="setting-' in body, f"{tab}: no search anchors rendered"
|
|
# Every settings field should be both anchored and tooltipped; tooltip count
|
|
# should not exceed anchor count (each field has at most one help_tip).
|
|
anchors = body.count('id="setting-')
|
|
tips = body.count('class="help-tip"')
|
|
assert tips >= 1 and anchors >= 1
|
|
assert tips <= anchors, f"{tab}: more tooltips ({tips}) than anchors ({anchors})"
|
|
|
|
|
|
@pytest.mark.parametrize("tab", SETTINGS_TABS)
|
|
def test_settings_partial_has_per_tab_filter(client, tab):
|
|
body = client.get(f"/v3/partials/{tab}").get_data(as_text=True)
|
|
assert 'class="settings-filter' in body, f"{tab}: per-tab filter box missing"
|
|
|
|
|
|
def test_display_tooltip_carries_rich_text(client):
|
|
# The brightness tooltip should include the authored guidance, not just a label.
|
|
body = client.get("/v3/partials/display").get_data(as_text=True)
|
|
assert 'id="setting-display-brightness"' in body
|
|
assert "Recommended:" in body # rich detail authored into a tooltip
|