mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
fix(instances): the address search shows host names as they are
The dialog filled its texts with String.replace, which reads $& and $' in the replacement as patterns: a host named c$&1 showed as c%(hosts)s1. The values now go in through a function, which inserts them as they are.
This commit is contained in:
parent
8063dcec66
commit
ad43352b09
2 changed files with 61 additions and 6 deletions
50
instances/tests/test_find_address_js.py
Normal file
50
instances/tests/test_find_address_js.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
"""The address search dialog's script (static/js/find_address.js) fills its
|
||||
texts with what the search answered, taken as plain text."""
|
||||
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT = Path(__file__).resolve().parents[2] / "static" / "js" / "find_address.js"
|
||||
|
||||
RUN = """
|
||||
const vm = require("vm");
|
||||
const text = () => ({textContent: "", classList: {toggle() {}}});
|
||||
const handlers = {};
|
||||
const ids = {
|
||||
"find-address": {dataset: DATA, addEventListener() {}},
|
||||
"find-address-form": {elements: {q: {value: "192.0.2.10"}}, querySelector: () => ({}),
|
||||
addEventListener: (name, fn) => { handlers[name] = fn; }},
|
||||
"find-address-error": text(),
|
||||
"find-address-results": {tBodies: [{replaceChildren() {}}], classList: {toggle() {}}},
|
||||
"find-address-coverage": text(),
|
||||
};
|
||||
const answer = ANSWER;
|
||||
vm.runInNewContext(SCRIPT, vm.createContext({
|
||||
document: {getElementById: (id) => ids[id]}, URLSearchParams, Promise, Date, location: {},
|
||||
fetch: () => Promise.resolve({redirected: false, json: () => Promise.resolve(answer)}),
|
||||
}));
|
||||
handlers.submit({preventDefault() {}});
|
||||
setTimeout(() => console.log(JSON.stringify(ids["find-address-coverage"].textContent)), 50);
|
||||
"""
|
||||
|
||||
DATA = {"url": "/find", "failed": "failed", "none": "None found.", "noneMac": "No MAC.", "unregistered": "(unregistered)",
|
||||
"conflict": "conflict", "checked": "Checked: %(n)s.", "noAgent": "No agent: %(n)s.",
|
||||
"unread": "Unread: %(n)s.", "hostsDown": "Not reached: %(hosts)s.",
|
||||
"hostsUnfinished": "Not finished: %(hosts)s.", "readAt": "Read.", "someUnchecked": "Some unchecked."}
|
||||
|
||||
|
||||
@unittest.skipUnless(shutil.which("node"), "node is not installed")
|
||||
class FindAddressScriptTests(unittest.TestCase):
|
||||
def test_host_names_are_put_in_as_they_are(self):
|
||||
# String.replace reads $& and $' in a replacement as patterns
|
||||
answer = {"matches": [], "read_at": "2026-10-25T10:00:00Z",
|
||||
"coverage": {"checked": 2, "hosts_down": ["c$&1"], "hosts_unfinished": ["d$'2"]}}
|
||||
js = (RUN.replace("SCRIPT", json.dumps(SCRIPT.read_text())).replace("DATA", json.dumps(DATA))
|
||||
.replace("ANSWER", json.dumps(answer)))
|
||||
out = subprocess.run(["node", "-e", js], capture_output=True, text=True, timeout=30)
|
||||
self.assertEqual(out.returncode, 0, out.stderr)
|
||||
self.assertEqual(json.loads(out.stdout),
|
||||
"None found. Checked: 2. Not reached: c$&1. Not finished: d$'2. Read.")
|
||||
|
|
@ -11,6 +11,11 @@
|
|||
var text = dialog.dataset;
|
||||
var MAC = /^[0-9a-f]{2}([:-]?)[0-9a-f]{2}(\1[0-9a-f]{2}){4}$/i; // as instances/addresses.py reads one
|
||||
|
||||
// a text with its placeholder filled; a function keeps $& and $' in the value as they are
|
||||
function fill(template, placeholder, value) {
|
||||
return template.replace(placeholder, function () { return value; });
|
||||
}
|
||||
|
||||
function cell(row, content) {
|
||||
var td = document.createElement("td");
|
||||
if (content instanceof Node) {
|
||||
|
|
@ -33,18 +38,18 @@
|
|||
}
|
||||
|
||||
function coverageText(c) {
|
||||
var lines = [text.checked.replace("%(n)s", c.checked)];
|
||||
var lines = [fill(text.checked, "%(n)s", c.checked)];
|
||||
if (c.no_agent) {
|
||||
lines.push(text.noAgent.replace("%(n)s", c.no_agent));
|
||||
lines.push(fill(text.noAgent, "%(n)s", c.no_agent));
|
||||
}
|
||||
if (c.unread) {
|
||||
lines.push(text.unread.replace("%(n)s", c.unread));
|
||||
lines.push(fill(text.unread, "%(n)s", c.unread));
|
||||
}
|
||||
if (c.hosts_down && c.hosts_down.length) {
|
||||
lines.push(text.hostsDown.replace("%(hosts)s", c.hosts_down.join(", ")));
|
||||
lines.push(fill(text.hostsDown, "%(hosts)s", c.hosts_down.join(", ")));
|
||||
}
|
||||
if (c.hosts_unfinished && c.hosts_unfinished.length) {
|
||||
lines.push(text.hostsUnfinished.replace("%(hosts)s", c.hosts_unfinished.join(", ")));
|
||||
lines.push(fill(text.hostsUnfinished, "%(hosts)s", c.hosts_unfinished.join(", ")));
|
||||
}
|
||||
if (c.some_unchecked) {
|
||||
lines.push(text.someUnchecked);
|
||||
|
|
@ -76,7 +81,7 @@
|
|||
});
|
||||
table.classList.toggle("d-none", matches.length === 0);
|
||||
if (answer.coverage) {
|
||||
var readAt = text.readAt.replace("%(time)s", new Date(answer.read_at).toLocaleTimeString());
|
||||
var readAt = fill(text.readAt, "%(time)s", new Date(answer.read_at).toLocaleTimeString());
|
||||
var none = MAC.test(form.elements.q.value.trim()) ? text.noneMac : text.none;
|
||||
coverage.textContent = (matches.length ? "" : none + " ") + coverageText(answer.coverage) + " " + readAt;
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue