1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 19:56:48 +00:00

fix(instances): the VM page opens the tab a redirect points to, and host links are for administrators

Redirects to #access (root password, SSH key) and other top-level panes
opened nothing, and some sub-tab ids did not exist. static/js/instance_tabs.js
opens the pane and the tab that holds it from the page structure, and
opens Settings on its first section, without leaving the current tab;
guest OS info is loaded whenever it is the section opened. The bottom bar linked to
host pages, which only administrators may open, and showed host names to
everyone. A stray </li> is removed; the page's inline scripts and the tab
logic are checked with Node.
This commit is contained in:
catborise 2026-10-07 23:57:10 +03:00
parent 70bed79c60
commit 44b3b6569a
7 changed files with 181 additions and 31 deletions

View file

@ -0,0 +1,77 @@
// node instance_tabs.test.js path/to/static/js/instance_tabs.js
const assert = require("assert");
const {restoreInstanceTab} = require(process.argv[2]);
// A minimal DOM: elements with an id, classes, a parent, children, and the
// few selectors restoreInstanceTab uses.
function el(id, classes, children = [], attrs = {}) {
const node = {id, classList: new Set(classes), children, attrs, parentElement: null};
node.classList.contains = (c) => node.classList.has(c);
node.getAttribute = (name) => attrs[name];
for (const child of children) child.parentElement = node;
node.querySelector = (selector) => find(node, selector);
return node;
}
function* walk(node) {
for (const child of node.children) {
yield child;
yield* walk(child);
}
}
function matches(node, selector) {
if (selector === ".nav-tabs") return node.classList.has("nav-tabs");
if (selector === ".nav-link.active") return node.classList.has("nav-link") && node.classList.has("active");
if (selector === ".nav-link:not(#osinfo-tab)") return node.classList.has("nav-link") && node.id !== "osinfo-tab";
if (selector === "#osinfo-tab") return node.id === "osinfo-tab";
throw new Error("unsupported selector " + selector);
}
function find(root, selector) {
for (const node of walk(root)) if (matches(node, selector)) return node;
return null;
}
function link(target, extra = []) {
return el(target === "#osinfo" ? "osinfo-tab" : "", ["nav-link", ...extra], [], {"data-bs-target": target});
}
// The VM page: top-level panes, Settings with its own tabs and panes.
function page(settingsLinks) {
const settings = el("settings", ["tab-pane"], [
el("", ["nav-tabs"], settingsLinks),
el("", ["tab-content"], [el("osinfo", ["tab-pane"]), el("options", ["tab-pane"]), el("vncsettings", ["tab-pane"])]),
]);
const root = el("", [], [el("", ["tab-content"], [el("power", ["tab-pane", "active"]), el("access", ["tab-pane"]), settings])]);
root.getElementById = (id) => {
for (const node of walk(root)) if (node.id === id) return node;
return null;
};
return root;
}
function run(doc, hash) {
const shown = [];
let loaded = 0;
restoreInstanceTab(doc, hash, (target) => shown.push(target), () => loaded++);
return {shown, loaded};
}
const settled = () => page([link("#options", ["active"])]);
// a top-level pane opens directly
assert.deepStrictEqual(run(settled(), "#access").shown, ["#access"]);
// a pane inside Settings opens Settings first
assert.deepStrictEqual(run(settled(), "#options").shown, ["#settings", "#options"]);
// no hash, an unknown one, or one with odd characters opens nothing
for (const hash of ["", "#", "#nosuchpane", '#a"]b']) {
assert.deepStrictEqual(run(settled(), hash).shown, [], hash);
}
// Settings without an active section opens its first one, not OS info
assert.deepStrictEqual(run(page([link("#osinfo"), link("#vncsettings")]), "").shown, ["#vncsettings"]);
// OS info as the only section is selected and loaded
assert.deepStrictEqual(run(page([link("#osinfo")]), ""), {shown: ["#osinfo"], loaded: 1});
// a redirect to OS info loads it
assert.deepStrictEqual(run(settled(), "#osinfo"), {shown: ["#settings", "#osinfo"], loaded: 1});
console.log("ok");

View file

@ -0,0 +1,23 @@
"""The VM page opens the tab a redirect points to (static/js/instance_tabs.js).
Its behaviour is checked with Node."""
import shutil
import subprocess
import unittest
from pathlib import Path
from django.conf import settings
BASE = Path(settings.BASE_DIR)
class InstanceTabsTestCase(unittest.TestCase):
@unittest.skipUnless(shutil.which("node"), "node is not installed")
def test_instance_tabs_js(self):
result = subprocess.run(
["node", str(BASE / "webvirtcloud/js_tests/instance_tabs.test.js"), str(BASE / "static/js/instance_tabs.js")],
capture_output=True,
text=True,
timeout=60,
)
self.assertEqual(result.returncode, 0, result.stderr)