Files

56 lines
2.1 KiB
JavaScript

const assert = require('node:assert/strict');
const { readFileSync } = require('node:fs');
const { test } = require('node:test');
const vm = require('node:vm');
const ts = require('typescript');
test('service status does not cause its parent form to be saved twice', async () => {
const source = readFileSync(new URL('../utils/base/luci/service.ts', `file://${__filename}`), 'utf8');
const { outputText } = ts.transpileModule(source, {
compilerOptions: { module: ts.ModuleKind.CommonJS, esModuleInterop: true }
});
function E(tag, attrs, children) {
const node = { tag, attrs, children: Array.isArray(children) ? children : [children] };
for (const child of node.children) {
if (child && typeof child === 'object') child.parentNode = node;
}
return node;
}
const sandbox = {
exports: {}, E, _: value => value,
window: { setTimeout() {} },
require(name) {
if (name === 'rpc') return { declare: () => () => Promise.resolve({}) };
if (name === 'poll') return { add() {} };
return {};
}
};
vm.runInNewContext(outputText, sandbox);
const status = sandbox.exports.ServiceUtils.renderServiceStatus('dns')();
const root = E('div', { class: 'cbi-map' }, status);
let saves = 0;
let deletes = 0;
let customOptionExists = true;
root.instance = { async save() {
saves++;
deletes++;
assert.ok(customOptionExists, 'duplicate delete would return UCI not found');
customOptionExists = false;
} };
const maps = [];
function visit(node) {
if (!node || typeof node !== 'object') return;
if ((node.attrs.class || '').split(/\s+/).includes('cbi-map')) maps.push(node);
node.children.forEach(visit);
}
visit(root);
// LuCI handleSave scans .cbi-map; findClassInstance walks up to the owner.
await Promise.all(maps.map(node => {
while (!node.instance) node = node.parentNode;
return node.instance.save();
}));
assert.equal(saves, 1);
assert.equal(deletes, 1);
assert.equal(status.children[0].attrs.class, 'cbi-section');
});