Files
RustProxy/tools/checks/release_0318_ui_test.mjs
T
2026-10-12 14:57:37 +02:00

64 lines
3.1 KiB
JavaScript

#!/usr/bin/env node
// JS-only regression of the Stored Logs sorting interaction (no Rust server).
import assert from 'node:assert/strict';
import fs from 'node:fs';
import vm from 'node:vm';
const read = name => fs.readFileSync(name, 'utf8');
const ui = read('src/admin/web/file-logs.js');
const css = read('src/admin/web/style.css');
const api = JSON.parse(read('src/admin/openapi.json'));
const params = api.paths['/api/file-logs/query'].get.parameters.map(param => param.name);
assert.ok(params.includes('sort_by') && params.includes('sort_descending'));
assert.ok(api.components.schemas.AdminPreferences.properties.file_log_sort);
assert.ok(api.components.schemas.AdminPreferences.properties.file_log_descending);
assert.match(ui, /params\.set\("sort_by", sortBy\)/);
assert.match(ui, /params\.set\("sort_descending", String\(sortDescending\)\)/);
assert.match(css, /file-log-sort-button/);
class FakeNode {
constructor(tag, text = '', cls = '') { this.tag = tag; this.textContent = text; this.className = cls; this.children = []; this.dataset = {}; this.attrs = {}; this.events = {}; }
setAttribute(name, value) { this.attrs[name] = value; }
append(...nodes) { this.children.push(...nodes); }
replaceChildren(...nodes) { this.children = nodes; }
addEventListener(name, handler) { this.events[name] = handler; }
click() { this.events.click?.(); }
}
const row = new FakeNode('tr');
const sort = ui.slice(ui.indexOf(' function syncHeaders() {'), ui.indexOf(' function syncColumnPicker()'));
assert.ok(sort.startsWith(' function syncHeaders() {') && sort.includes('file-log-sort-button'));
const ctx = {
el: (tag, text, cls) => new FakeNode(tag, text, cls),
$: () => ({querySelector: () => row}),
orderedColumns: () => ['time', 'status'],
visibleColumns: new Set(['time', 'status']),
specialLabels: new Map([['time', 'time'], ['status', 'status']]),
columnLabel: id => id,
t: key => key,
sortBy: 'time', sortDescending: true, selectedIndex: 3, offset: 100,
saved: 0, refreshed: 0,
savePreferences() { this.saved++; },
refresh() { this.refreshed++; },
};
// Methods capture explicit counters rather than relying on vm's `this` binding.
ctx.savePreferences = () => { ctx.saved++; };
ctx.refresh = reset => { if (reset) ctx.refreshed++; };
vm.createContext(ctx);
vm.runInContext(`${sort}\nglobalThis.draw = syncHeaders;`, ctx);
ctx.draw();
assert.equal(row.children.length, 2);
assert.equal(row.children[0].attrs['aria-sort'], 'descending');
assert.equal(row.children[1].attrs['aria-sort'], 'none');
row.children[1].children[0].click();
assert.equal(ctx.sortBy, 'status');
assert.equal(ctx.sortDescending, false);
assert.equal(ctx.offset, 0);
assert.equal(ctx.saved, 1);
assert.equal(ctx.refreshed, 1);
assert.equal(row.children[1].attrs['aria-sort'], 'ascending');
row.children[1].children[0].click();
assert.equal(ctx.sortDescending, true);
assert.equal(row.children[1].attrs['aria-sort'], 'descending');
assert.equal(ctx.saved, 2);
assert.equal(ctx.refreshed, 2);
console.log('PASS: 0.31.9 Stored Logs headers sort by column, toggle direction, reset pagination, persist preferences and request server sorting.');