mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-13 10:36:35 +00:00
Four commits landed on this branch from review while the layout wiring was in progress, and the two solved overlapping problems differently. Theirs made elementKeys() append every layout-only key, matched by exact name. Against football that also lists `score` and `odds` -- entries the alias resolution already pairs with score_text and odds_text -- so those offsets would get two controls posting the same field. The row split here (styleRows via core's x-layout-key, positionRows for what nothing claims) replaces that append; elementKeys() is back to listing style elements only. Theirs also covered cases this side had missed, all kept: - A leaf straight under layout (a show_logo toggle, no x/y object) now gets a position row with a self-keyed column bound to customization.layout.<key>. Core no longer lets an element claim a leaf as its offsets. - Leaf columns keep their namespaced id, so a leaf sharing a name with another entry's axis cannot be shadowed. - columnsFor(schema) still works with one argument (every row), so their column tests run unchanged. - The owned key is CSS.escape()'d before it becomes a selector. test_style_editor_element_keys.js is rewritten for the new contract, and says why the old one had to go. All six JS suites pass; in a browser football's 92 layout fields each render once, and a synthetic leaf -- including one named `scale` alongside a `scale` axis -- gets exactly one correctly named control. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
127 lines
5.3 KiB
JavaScript
127 lines
5.3 KiB
JavaScript
// Regression test for how style-editor.js decides which rows to draw,
|
|
// extracted verbatim from the shipped widget (own/ownObj/elementKeys/
|
|
// styleRows/positionRows have no DOM dependency) so the test can't drift from
|
|
// the real implementation.
|
|
//
|
|
// render() claims the whole `layout` child of a customization schema as the
|
|
// widget's own -- removing it from the generic fallback renderer entirely,
|
|
// because posting the same offset twice from two controls is worse than
|
|
// owning slightly too much. That is only safe when every entry under `layout`
|
|
// gets exactly one row:
|
|
//
|
|
// * styleRows(): one per style element, paired with the layout entry that
|
|
// holds its offsets. The pairing is not by exact name -- a hand-written
|
|
// schema styles 'score_text' but positions 'score' -- so core records it
|
|
// as x-layout-key, resolved through the same alias map the renderer reads
|
|
// offsets with.
|
|
// * positionRows(): one per layout entry no style element claimed -- a logo,
|
|
// a timeout indicator, a possession arrow, or a bare leaf like a
|
|
// show_logo toggle.
|
|
//
|
|
// This contract replaced an earlier one in which elementKeys() itself
|
|
// appended layout-only keys. That compared names exactly, so an aliased entry
|
|
// ('score' for 'score_text') was listed a second time and its offsets got two
|
|
// controls posting the same field.
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const V3 = path.resolve(__dirname, '../../../web_interface/static/v3');
|
|
|
|
const src = fs.readFileSync(path.join(V3, 'js/widgets/style-editor.js'), 'utf8');
|
|
const start = src.indexOf('function own(obj, key)');
|
|
const endMark = 'function control(opts)';
|
|
const end = src.indexOf(endMark);
|
|
if (start === -1 || end === -1) {
|
|
console.error('FAIL: could not locate own()..positionRows() in style-editor.js -- slice markers need updating');
|
|
process.exit(1);
|
|
}
|
|
eval(src.slice(start, end)); // defines own/ownObj/elementKeys/styleRows/positionRows
|
|
|
|
let failures = 0;
|
|
function ok(desc, cond) {
|
|
if (cond) { console.log(`ok - ${desc}`); }
|
|
else { console.log(`FAIL - ${desc}`); failures++; }
|
|
}
|
|
|
|
function layoutKeysOf(rows) { return rows.map(function (r) { return r.layoutKey; }); }
|
|
|
|
// score_text is styled and positioned under an alias; odds_text has no
|
|
// offsets at all; possession is positioned but never styled; show_logo is a
|
|
// bare leaf under layout.
|
|
const schema = {
|
|
properties: {
|
|
score_text: {
|
|
type: 'object', 'x-style-managed': true, 'x-layout-key': 'score',
|
|
properties: { font: { type: 'string' }, text_color: { type: 'array' } },
|
|
},
|
|
odds_text: {
|
|
type: 'object', 'x-style-managed': true,
|
|
properties: { font: { type: 'string' } },
|
|
},
|
|
layout: {
|
|
type: 'object',
|
|
'x-propertyOrder': ['score', 'possession', 'show_logo'],
|
|
properties: {
|
|
score: { type: 'object', properties: { x_offset: {}, y_offset: {} } },
|
|
possession: { type: 'object', properties: { x_offset: {}, y_offset: {} } },
|
|
show_logo: { type: 'boolean', default: true },
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const keys = elementKeys(schema);
|
|
ok('elementKeys lists the style elements', keys.join(',') === 'score_text,odds_text');
|
|
ok('layout and modes containers are never treated as elements',
|
|
keys.indexOf('layout') === -1 && keys.indexOf('modes') === -1);
|
|
|
|
const styled = styleRows(schema);
|
|
ok('a style element is paired with the layout entry core resolved for it',
|
|
styled[0].key === 'score_text' && styled[0].layoutKey === 'score');
|
|
ok('a style element with no offsets claims nothing', styled[1].layoutKey === null);
|
|
|
|
const positions = positionRows(schema);
|
|
ok('a layout-only object entry gets a position row',
|
|
layoutKeysOf(positions).indexOf('possession') !== -1);
|
|
ok('a layout-only leaf gets a position row too',
|
|
layoutKeysOf(positions).indexOf('show_logo') !== -1);
|
|
ok('an entry claimed through an alias is not listed again as a position',
|
|
layoutKeysOf(positions).indexOf('score') === -1);
|
|
ok('positions follow the declared order',
|
|
layoutKeysOf(positions).join(',') === 'possession,show_logo');
|
|
|
|
const every = layoutKeysOf(styled).filter(Boolean).concat(layoutKeysOf(positions));
|
|
ok('every layout entry gets exactly one row',
|
|
every.length === new Set(every).size
|
|
&& every.slice().sort().join(',') === ['possession', 'score', 'show_logo'].join(','));
|
|
|
|
// A schema with no layout block at all must behave exactly as before --
|
|
// the overwhelmingly common case must not gain a phantom row.
|
|
const noLayout = {
|
|
properties: {
|
|
score_text: {
|
|
type: 'object', 'x-style-managed': true,
|
|
properties: { font: { type: 'string' } },
|
|
},
|
|
},
|
|
};
|
|
ok('a schema without a layout block has no positions',
|
|
elementKeys(noLayout).join(',') === 'score_text' && positionRows(noLayout).length === 0);
|
|
|
|
// Without an annotation (the compact declaration, or a schema that never went
|
|
// through expansion) the two blocks share one key.
|
|
const unannotated = {
|
|
properties: {
|
|
home_logo: { type: 'object', 'x-style-managed': true, properties: {} },
|
|
layout: {
|
|
type: 'object',
|
|
properties: { home_logo: { type: 'object', properties: { x_offset: {}, scale: {} } } },
|
|
},
|
|
},
|
|
};
|
|
ok('an exact-name layout entry is claimed without an annotation',
|
|
styleRows(unannotated)[0].layoutKey === 'home_logo'
|
|
&& positionRows(unannotated).length === 0);
|
|
|
|
if (failures) { console.log(`\n${failures} failure(s)`); process.exit(1); }
|
|
console.log('\nall checks passed');
|