mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-05 23:05:10 +00:00
* feat(web): the Display tab is an ES-module page, with a page-visibility service (stage 4) - core/visibility.js: each page gets ctx.visibility (whileVisible, every, isVisible). Work registered there runs only while the page's tab is active and the browser tab visible, and ends when the page is swapped out. It reads the active tab from window.LEDVisibility, so it agrees with the classic partials. The registry gained a mountContext option for per-mount services. - pages/display.js replaces display.html's two inline scripts. The 5 s sync status poll runs through ctx.visibility.every; the status and scroll-speed hint requests go through ctx.api with ctx.signal, as does the Vegas order widget's plugin-list request. The Advanced toggle is a delegated data-action; window.updateSyncUI is a deprecated alias. - New DOM suites test_visibility_service.js and test_display_page.js; test_display_partial_ids.js imports the module. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(web): no computed keys in the Display page's readout and destroy Codacy's object-injection rule flagged v[id] and ctx.state[name]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
85 lines
4.0 KiB
JavaScript
Executable File
85 lines
4.0 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://<pi-ip>:5000 node run_all.js point the DOM suites at a rig
|
|
// REQUIRE_DOM=1 node run_all.js fail, rather than skip, when the DOM
|
|
// suites can't run (CI sets this)
|
|
//
|
|
// 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_plugin_order_list.js',
|
|
'unit/test_html_escaping.js', 'unit/test_style_editor_element_keys.js',
|
|
'unit/test_style_editor_layout_leaf_columns.js',
|
|
'unit/test_style_editor_layout_leaf_collision.js',
|
|
'unit/test_update_all.js',
|
|
'unit/test_store_install.js',
|
|
'unit/test_install_polling.js',
|
|
'unit/test_store_categories.js',
|
|
'unit/test_github_url_install.js',
|
|
'unit/test_inline_handler_escaping.js',
|
|
'unit/test_plugin_action_delegation.js', 'unit/test_file_upload_widget.js',
|
|
'unit/test_store_registry_fields.js', 'unit/test_restart_banner.js',
|
|
'unit/test_page_registry.js', 'unit/test_core_modules.js',
|
|
'unit/test_overview_reconciliation_poll.js',
|
|
'unit/test_display_partial_ids.js',
|
|
'unit/test_general_web_login_token.js'];
|
|
const DOM = ['dom/test_installed_dom.js', 'dom/test_store_dom.js', 'dom/test_no_double_fetch.js',
|
|
'dom/test_tools_sections.js', 'dom/test_cache_page.js',
|
|
'dom/test_durations_page.js', 'dom/test_operation_history_page.js',
|
|
'dom/test_raw_json_page.js', 'dom/test_backup_restore_page.js',
|
|
'dom/test_schedule_page.js', 'dom/test_general_page.js',
|
|
'dom/test_visibility_service.js', 'dom/test_display_page.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 (process.env.REQUIRE_DOM && (!haveJsdom || !up)) {
|
|
// A skipped suite reads as a pass in CI; say why and fail instead.
|
|
results.push([haveJsdom ? `DOM suites (no web interface at ${BASE})`
|
|
: 'DOM suites (jsdom not installed)', false]);
|
|
} else 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');
|
|
})();
|