mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 14:25:08 +00:00
A prior fix on this PR made elementKeys() append any layout-only key with
no style block of its own (a logo, a timeout indicator, a possession
arrow), so table() draws a row for it instead of losing it when the
wholesale `layout` claim removes the generic fallback. That covers a
layout-only key shaped like an object (x_offset/y_offset, ...), because
columnsFor() only ever produced columns from a key's *sub-fields*.
It missed the case where the layout-only key's own value is itself a
leaf -- a plain "show_logo" boolean directly under layout, no x/y object
underneath. elementKeys() still lists it (any row: no matching column),
so it renders as an uneditable blank row and its only control -- the
generic fallback checkbox -- is still gone. Confirmed by executing the
real widget's render() against a synthetic schema in Node (a DOM-stub
harness, not committed): the field's name never appeared as an <input>.
columnsFor() now gives such a leaf key a column keyed to itself
('layout-leaf'), and elementRow() binds it to the leaf's own path
(customization.layout.<key>, matching the name the fallback would have
used) instead of leaving every cell blank.
New regression test (test/js/unit/test_style_editor_layout_leaf_columns.js,
following this PR's existing eval-extraction pattern) checks the leaf
column is produced, is self-keyed, doesn't duplicate, and that a schema
with no leaf-valued layout key is unaffected; wired into run_all.js and
the suite table in test/js/README.md.
test/js/run_all.js: 84 + 6 + 6 = all suites passed (jsdom unavailable
here, DOM suites skip as before). Python suite untouched by this change;
test_style_editor_extra_fields.py, test_style_editor_save_roundtrip.py
and the one PIL-dependent style_editor_takeover.py case fail identically
before this commit -- missing flask/PIL in this sandbox, not this PR.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
61 lines
2.5 KiB
JavaScript
Executable File
61 lines
2.5 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
// Runs the web-interface JS suites.
|
|
//
|
|
// node run_all.js unit suites, plus DOM suites if a
|
|
// web interface is reachable
|
|
// BASE=http://10.0.10.169:5000 node run_all.js point the DOM suites at a rig
|
|
//
|
|
// Unit suites need nothing but node. The DOM suites need `npm install` (jsdom)
|
|
// and a running web interface, because they deliberately test against the real
|
|
// server-rendered HTML and the real API rather than fixtures.
|
|
const { spawnSync } = require('child_process');
|
|
const http = require('http');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
|
|
const BASE = process.env.BASE || 'http://localhost:5000';
|
|
const UNIT = ['unit/test_list_filter.js', 'unit/test_render_cards.js',
|
|
'unit/test_html_escaping.js', 'unit/test_style_editor_element_keys.js',
|
|
'unit/test_style_editor_layout_leaf_columns.js'];
|
|
const DOM = ['dom/test_installed_dom.js', 'dom/test_store_dom.js', 'dom/test_no_double_fetch.js',
|
|
'dom/test_tools_sections.js'];
|
|
|
|
function reachable(url) {
|
|
return new Promise(res => {
|
|
const req = http.get(url, r => { r.resume(); res(r.statusCode < 500); });
|
|
req.on('error', () => res(false));
|
|
req.setTimeout(4000, () => { req.destroy(); res(false); });
|
|
});
|
|
}
|
|
|
|
function run(file) {
|
|
const r = spawnSync(process.execPath, [path.join(__dirname, file)],
|
|
{ stdio: 'inherit', cwd: __dirname, env: process.env });
|
|
return r.status === 0;
|
|
}
|
|
|
|
(async () => {
|
|
const results = [];
|
|
for (const f of UNIT) results.push([f, run(f)]);
|
|
|
|
const haveJsdom = fs.existsSync(path.join(__dirname, 'node_modules', 'jsdom'));
|
|
const up = await reachable(BASE + '/');
|
|
|
|
if (!haveJsdom) {
|
|
console.log(`\nSKIPPING DOM suites: jsdom not installed (run: npm install)\n`);
|
|
} else if (!up) {
|
|
console.log(`\nSKIPPING DOM suites: no web interface reachable at ${BASE}`);
|
|
console.log(` start one with: EMULATOR=true python3 web_interface/app.py`);
|
|
console.log(` or point at a rig: BASE=http://<host>:5000 node run_all.js\n`);
|
|
} else {
|
|
console.log(`\nDOM suites against ${BASE}\n`);
|
|
for (const f of DOM) results.push([f, run(f)]);
|
|
}
|
|
|
|
const failed = results.filter(([, ok]) => !ok);
|
|
console.log('\n─── summary ───');
|
|
results.forEach(([f, ok]) => console.log(` ${ok ? 'PASS' : 'FAIL'} ${f}`));
|
|
if (failed.length) { console.log(`\n${failed.length} suite(s) failed\n`); process.exit(1); }
|
|
console.log('\nall suites passed\n');
|
|
})();
|