73 lines
4.9 KiB
JavaScript
73 lines
4.9 KiB
JavaScript
// Run with: node tests/acl_picker_dom.cjs. Tiny DOM stub for picker state/regression.
|
|
"use strict";
|
|
const assert = require("node:assert/strict");
|
|
const fs = require("node:fs");
|
|
const vm = require("node:vm");
|
|
const path = require("node:path");
|
|
class DomEvent {
|
|
constructor(type, options = {}) { this.type = type; this.bubbles = !!options.bubbles; this.target = null; }
|
|
}
|
|
class DomNode {
|
|
constructor(tag) { this.tag = tag; this.className = ""; this.children = []; this.parent = null; this.dataset = {}; this.handlers = new Map(); this.hidden = false; this.disabled = false; this.checked = false; this.value = ""; this.type = ""; this._text = ""; }
|
|
get textContent() { return this._text + this.children.map(c => c.textContent).join(""); }
|
|
set textContent(text) { this.replaceChildren(); this._text = String(text); }
|
|
append(...children) { for (const child of children) { child.parent = this; this.children.push(child); } }
|
|
replaceChildren(...children) { for (const child of this.children) child.parent = null; this.children = []; this._text = ""; this.append(...children); }
|
|
setAttribute() { }
|
|
focus() { }
|
|
addEventListener(type, handler) { if (!this.handlers.has(type)) this.handlers.set(type, []); this.handlers.get(type).push(handler); }
|
|
dispatchEvent(event) { if (!event.target) event.target = this; for (const handler of this.handlers.get(event.type) || []) handler(event); if (event.bubbles && this.parent) this.parent.dispatchEvent(event); }
|
|
click() { this.dispatchEvent(new DomEvent("click", {bubbles:true})); }
|
|
hasClass(name) { return this.className.split(/\s+/).includes(name); }
|
|
matches(selector) { return selector === 'input[type="checkbox"]' && this.tag === "input" && this.type === "checkbox"; }
|
|
all() { return this.children.flatMap(child => [child, ...child.all()]); }
|
|
querySelectorAll(selector) {
|
|
const descendants = this.all();
|
|
if (selector === '.acl-picker-option input[type="checkbox"]:checked') return descendants.filter(n => n.tag === "input" && n.type === "checkbox" && n.checked && n.parent?.hasClass("acl-picker-option"));
|
|
if (selector === '.acl-picker-option input[type="checkbox"]') return descendants.filter(n => n.tag === "input" && n.type === "checkbox" && n.parent?.hasClass("acl-picker-option"));
|
|
const value = selector.match(/^\.acl-picker-option input\[value="([^"]+)"\]$/);
|
|
if (value) return descendants.filter(n => n.tag === "input" && n.value === value[1] && n.parent?.hasClass("acl-picker-option"));
|
|
const cls = selector.match(/^\.([\w-]+)$/);
|
|
if (cls) return descendants.filter(n => n.hasClass(cls[1]));
|
|
throw Error("Unsupported test selector " + selector);
|
|
}
|
|
querySelector(selector) { return this.querySelectorAll(selector)[0] || null; }
|
|
}
|
|
const profiles = Array.from({length:10}, (_,i) => ({
|
|
id: `profile_${i}`, name: i === 0 ? "Office networks" : `Profile ${i}`,
|
|
acl: {default: "deny", rules:[{network:`10.${i}.0.0/16`,action:"allow"},{network:"192.0.2.0/24",action:"deny"}]},
|
|
country_access: {mode: i === 0 ? "block" : "off", countries: i === 0 ? ["RU"] : []}
|
|
}));
|
|
const context = {
|
|
window: {},
|
|
envelope: {config:{acl_profiles:profiles}},
|
|
el(tag, text, cls) { const node = new DomNode(tag); if (text !== undefined) node.textContent = text; if (cls) node.className = cls; return node; },
|
|
t: key => key,
|
|
Event: DomEvent,
|
|
};
|
|
vm.createContext(context);
|
|
vm.runInContext(fs.readFileSync(path.resolve(__dirname, "../src/admin/web/acl-picker.js"), "utf8"), context);
|
|
const picker = context.window.rustProxyAclPicker;
|
|
const root = picker.create(["profile_0", "profile_1"], "vh-acl-profile-choices");
|
|
assert.deepEqual(Array.from(picker.read(root)), ["profile_0", "profile_1"]);
|
|
assert.equal(root.querySelectorAll(".acl-picker-tag").length, 2);
|
|
assert.match(root.textContent, /10\.0\.0\.0\/16/);
|
|
assert.match(root.textContent, /RU/);
|
|
const search = root.querySelector(".acl-picker-search");
|
|
search.value = "Office"; search.dispatchEvent(new DomEvent("input", {bubbles:true}));
|
|
assert.equal(root.querySelectorAll(".acl-picker-option").filter(node => !node.hidden).length, 1);
|
|
search.value = ""; search.dispatchEvent(new DomEvent("input", {bubbles:true}));
|
|
root.querySelector(".acl-picker-remove").click();
|
|
assert.deepEqual(Array.from(picker.read(root)), ["profile_1"]);
|
|
for (const check of root.querySelectorAll('.acl-picker-option input[type="checkbox"]').slice(2,9)) {
|
|
check.checked = true;
|
|
check.dispatchEvent(new DomEvent("change", {bubbles:true}));
|
|
}
|
|
assert.equal(picker.count(root), 8);
|
|
assert.equal(root.querySelector('.acl-picker-option input[value="profile_9"]').disabled, true);
|
|
root.querySelector(".acl-picker-remove").click();
|
|
assert.equal(root.querySelector('.acl-picker-option input[value="profile_9"]').disabled, false);
|
|
const docker = picker.create([], "docker-acl-profile");
|
|
assert.deepEqual(Array.from(picker.read(docker)), []); // empty override inherits agent profiles
|
|
console.log("ACL picker DOM regression: PASS (choices, preview, search, badges, limit and inheritance)");
|