#!/usr/bin/env node
// 0.31.7 DOM/source + executable revision-diff tests (no Rust runtime).
import assert from 'node:assert/strict';
import fs from 'node:fs';
import vm from 'node:vm';
const read = path => fs.readFileSync(path, 'utf8');
const html = read('src/admin/web/index.html');
const css = read('src/admin/web/style.css');
const settings = read('src/admin/web/settings.js');
const hist = read('src/admin/web/app/config-history.js');
const management = read('src/admin/web/management.js');
const listener = read('src/admin/web/integrations.js');
const router = read('src/admin/router.rs');
const core = read('src/admin/web/vhosts/core.js');
const editor = read('src/admin/web/vhosts/editor.js');
const rates = read('src/admin/web/vhosts/rates.js');
const model = read('src/rate_limit.rs');
const api = JSON.parse(read('src/admin/openapi.json'));
assert.match(html, /data-settings-tab="policies"/);
for (const name of ['user-agent', 'headers', 'response']) {
assert.match(html, new RegExp(`data-policy-settings-tab="${name}"`));
assert.match(html, new RegExp(`data-policy-settings-page="${name}"`));
}
for (const form of ['global-ua-block-form', 'headers-form', 'response-form']) {
assert.equal(html.split(`id="${form}"`).length, 2, `${form} must be unique`);
}
assert.match(settings, /selectPolicyTab\("user-agent"\)/);
assert.match(html, /id="config-history-json-dialog"/);
assert.match(html, /id="config-history-compare-open"/);
const actionStart = html.indexOf('
');
const actionEnd = html.indexOf('
', actionStart);
const restorePos = html.indexOf('id="config-history-restore"', actionStart);
const comparePos = html.indexOf('id="config-history-compare-open"', actionStart);
assert.ok(actionStart > -1 && restorePos > actionStart && comparePos > restorePos && comparePos < actionEnd,
'JSON compare button must appear directly under restore inside the same action group');
assert.equal(html.split('id="config-history-compare-open"').length, 2);
assert.match(html, /id="config-json-compare-title"[\s\S]*?[\s\S]*?id="config-history-json-close"/);
assert.match(css, /dialog\.config-history-dialog,\s*dialog\.config-history-json-dialog\s*\{/);
assert.match(css, /\.config-history-dialog\[open\]\s*\{[^}]*grid-template-rows:\s*auto minmax\(0, 1fr\)/);
assert.match(css, /\.config-history-diff\s*\{[^}]*overflow-y:\s*auto/);
assert.match(css, /\.config-history-layout\s*\{[^}]*overflow:\s*hidden/);
assert.match(css, /@media \(max-width:\s*860px\)\s*\{[\s\S]*?\.config-history-layout\s*\{[^}]*overflow-y:\s*auto/);
assert.match(css, /\.config-history-json-columns pre\s*\{[^}]*border:\s*0/);
assert.match(hist, /"config-history-json-before"\)\.textContent/);
assert.match(hist, /"config-history-json-after"\)\.textContent/);
assert.match(css, /\.listener-protocol-settings-actions\s*\{[^}]*gap:\s*14px/);
assert.match(listener, /listener-protocol-settings-actions/);
assert.match(management, /el\("details", undefined, "acl-profile-attached"\)/);
assert.doesNotMatch(management, /attached\.open\s*=\s*true/);
assert.match(router, /include_str!\("web\/vhosts\/rates\.js"\)/);
assert.match(editor, /user_agent_limits: uaRatesReader\(\)/);
assert.match(editor, /userAgentRateEditor\(g, rate\.user_agent_limits \|\| \[\]\)/);
assert.match(core, /uaRatesReader/);
assert.match(rates, /function userAgentRateEditor/);
assert.match(model, /pub user_agent_limits: Vec/);
assert.ok(api.components.schemas.VhostRateLimit.properties.user_agent_limits);
const start = hist.indexOf('function configDiff('), end = hist.indexOf('function renderConfigRevisionDetail(', start);
assert.ok(start > 0 && end > start, 'executable diff function must exist');
const context = { before: null, after: null };
vm.createContext(context);
vm.runInContext(`${hist.slice(start, end)}\nglobalThis.diffResult = () => configDiff(before, after);`, context);
const diff = (before, after) => { context.before=before; context.after=after; return JSON.parse(JSON.stringify(context.diffResult())); };
let result = diff({port:80, x:{a:true, b:2}, vhosts:[{id:'v1', host:'a'},{id:'v2', host:'b'}]},
{port:443, x:{a:true, b:3}, vhosts:[{id:'v2', host:'b'},{id:'v1', host:'a'}]});
assert.deepEqual(result.map(x => x.path), ['$["port"]','$["x"]["b"]']);
result = diff({a:1, headers:[]}, {a:1, headers:[{id:'one',value:'ok'}], policy:true});
assert.deepEqual(result.map(x => x.path), ['$["headers"]["one"]["id"]','$["headers"]["one"]["value"]','$["policy"]']);
result = diff({items: [{id:'one', active: true}]}, {items: [{id:'one',active:false}]});
assert.equal(result.length,1);
assert.equal(result[0].path,'$["items"]["one"]["active"]');
assert.ok(diff({}, Object.fromEntries(Array.from({length:350},(_,i) => [i, i]))).length <= 200);
console.log('PASS: 0.31.7 Settings sections, vhost UA rate editor/API, ACL/listener layout, changed-field revision diff, bounded scrolling, conventional full JSON modal.');