# # Glances - An eye on your system # # SPDX-FileCopyrightText: 2026 Nicolas Hennion # # SPDX-License-Identifier: LGPL-3.0-only # """Glances v5 — the WebUI bundle rendered against a fake DOM. These tests run tests/fixtures/webui_render_probe.js under node against the built bundle; they never start the FastAPI app. Moved out of test_webserver_v5.py unchanged (G9-6 Task 0). """ from __future__ import annotations import json import shutil import subprocess from pathlib import Path import pytest from glances.outputs.curses_renderer_v5 import HEADER_SLOT_LEFT, HEADER_SLOT_RIGHT, LEFT_SLOT, RIGHT_SLOT, TOP_SLOT _BUNDLE_PATH = Path(__file__).parent.parent / "glances" / "outputs" / "static" / "public" / "glances5.js" _RENDER_PROBE_PATH = Path(__file__).parent / "fixtures" / "webui_render_probe.js" @pytest.mark.skipif(shutil.which("node") is None, reason="node not available") def test_the_v5_bundle_actually_renders_an_element(): """Guards against a silent, all-tests-green blank page. `import { createApp } from "vue"` resolves to Vue's runtime-only build unless webpack aliases it to the compiler-included build. A component that only supplies a string `template:` (as app_v5.js does) then gets `render = NOOP` -- in production mode the dev warning for this is compiled out, so nothing is logged and nothing throws. The mount target ends up holding a single, silently-empty comment node instead of real markup. Every HTTP-level test in this file (e.g. test_the_v5_bundle_is_served) only checks that the bundle is served with a non-empty body -- a bundle that renders nothing passes all of them. This test instead runs the actual bundle against a minimal DOM stub and asserts that the `#app` mount target ends up containing a real ELEMENT node, not just Vue's empty-render comment placeholder -- the distinction Finding 1 hinged on. G9-2's AppShell also renders a bare `
`, so a `tagName == "MAIN"` check alone would pass against a stub as blank as G9-1's -- e.g. a `createApp({ template: "
" })`. Assert markup that only the real shell (header + plugin area + footer) produces and that a blank page cannot satisfy: the `gl-app` class and both a HEADER and a FOOTER descendant. The fixture's `fetch` stub answers `api/5/alert` with twelve events in the real `_build_event()` shape (glances/alerts_v5.py:706-716), oldest first -- matching get_history()'s documented most-recent-LAST contract (glances/alerts_v5.py:181). Assert the footer actually renders them -- identified by plugin AND field, not just the bare level, so a fallback to a field that does not exist cannot pass unnoticed -- AND that it keeps the ten MOST RECENT, newest first: a single-alert stub could not catch AppShell using `history.slice(0, 10)` (the ten OLDEST) instead of the correct `history.slice(-10).reverse()`, which is exactly the bug that shipped in the previous fix round. This is exactly the shape the G9-1 blank page took: an untested corner of an otherwise-green test suite -- and the corner turned out deeper than the first probe fix realised. """ if not _BUNDLE_PATH.exists(): pytest.fail(f"{_BUNDLE_PATH} is missing -- run `npm run build` in glances/outputs/static/") result = subprocess.run( ["node", str(_RENDER_PROBE_PATH), str(_BUNDLE_PATH)], capture_output=True, text=True, timeout=30, ) assert result.returncode == 0, f"render probe crashed:\n{result.stderr}" # A thrown exception inside AppShell's async mounted() hook (e.g. a # sandbox missing a global it calls) does not necessarily flip the exit # code -- it can land as stderr noise on an otherwise-green run. Assert # stderr is empty so a broken lifecycle hook cannot hide behind a # passing returncode again. assert result.stderr == "", f"render probe printed to stderr:\n{result.stderr}" payload = json.loads(result.stdout) # nodeType 1 == ELEMENT_NODE, 8 == COMMENT_NODE (the runtime-only-Vue # failure mode). Assert the concrete element, not just "has children": # a broken build also has exactly one child -- an empty comment. assert payload["nodeType"] == 1, ( f"expected an ELEMENT_NODE under #app, got nodeType={payload['nodeType']!r} " f"(tagName={payload['tagName']!r}) -- the Vue template rendered nothing" ) assert payload["tagName"] == "MAIN" assert payload["hasClass"], "expected the root
to carry class 'gl-app'" assert payload["hasHeader"], "expected a
descendant of the app shell" assert payload["hasFooter"], "expected a