fix(web): widget cache-busting, dead frontend code, and dependency pins (#656)

- Plugin-supplied widgets load as /static/plugin-widgets/...js?v=<plugin
  version>, so an update isn't hidden behind the year-long immutable cache.
- Fire-and-forget loadInstalledPlugins() calls catch the rejection it has
  already reported, so the global handler no longer adds a second toast.
- Timezone picker renders again when the General partial is re-injected.
- Remove dead code: executePluginAction's six plugin-id fallbacks and
  [DEBUG] logging, window.currentPluginConfig and every read of it, the
  file-upload JSON delete branch, unused PluginAPI / PluginInstallManager /
  PluginStateManager helpers, loadPluginWidgetsFromManifest, the stale
  install_manager.js and LEDVisibility fallbacks, error_handler.js's global
  escapeHtml, 13 unused CSS rules, and stale comments/no-op returns.
- pytz < 2027, psutil < 7 in requirements-test.txt, pytest-cov < 8.
- Pin anthropics/claude-code-action to the commit v1 resolves to.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-28 10:40:52 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 65d82580bc
commit 0e9e2cabba
24 changed files with 109 additions and 579 deletions
+20 -1
View File
@@ -215,7 +215,7 @@ def config_form(tmp_path):
orig_cm = getattr(pv.pages_v3, "config_manager", None)
def _render(plugin_id="soccer-scoreboard", schema=None, widgets=None,
files=None):
files=None, version=None):
d = _make_plugin(tmp_path, plugin_id, widgets=widgets, files=files)
(d / "config_schema.json").write_text(
json.dumps(schema or {"type": "object", "properties": {}}),
@@ -224,6 +224,8 @@ def config_form(tmp_path):
pm = MagicMock()
pm.plugins_dir = str(tmp_path)
pm.get_plugin_info.return_value = {"id": plugin_id, "name": plugin_id}
if version is not None:
pm.get_plugin_info.return_value["version"] = version
pm.get_plugin.return_value = None
pv.pages_v3.plugin_manager = pm
@@ -261,6 +263,23 @@ class TestTheFormRequestsPluginWidgets:
assert "ensureWidget" in body
assert '"custom-leagues"' in body
def test_the_plugin_version_is_passed_to_the_loader(self, config_form):
"""/static/ is cached as immutable for a year, so the loader adds
?v=<version>; without it an updated widget never reaches the browser."""
r = config_form(schema=_schema_with_widget("custom-leagues"),
widgets=[{"name": "custom-leagues"}],
files={"custom-leagues.js": WIDGET_BODY},
version="1.4.2")
body = r.get_data(as_text=True)
assert 'var VERSION = "1.4.2";' in body
assert "ensureWidget(WIDGET, PLUGIN, VERSION)" in body
def test_a_plugin_without_a_version_passes_an_empty_one(self, config_form):
r = config_form(schema=_schema_with_widget("custom-leagues"),
widgets=[{"name": "custom-leagues"}],
files={"custom-leagues.js": WIDGET_BODY})
assert 'var VERSION = "";' in r.get_data(as_text=True)
def test_the_text_input_remains_as_the_fallback(self, config_form):
"""A widget that fails to load must not cost the user their value."""
r = config_form(schema=_schema_with_widget("custom-leagues"),