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:
parent
70bed79c60
commit
44b3b6569a
7 changed files with 181 additions and 31 deletions
77
webvirtcloud/js_tests/instance_tabs.test.js
Normal file
77
webvirtcloud/js_tests/instance_tabs.test.js
Normal 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");
|
||||
23
webvirtcloud/test_instance_tabs_js.py
Normal file
23
webvirtcloud/test_instance_tabs_js.py
Normal 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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue