feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g. pumpingStation) and a set of child dashboards (e.g. measurements), it now removes any non-row panel from the root whose meta.emittedFields are fully covered by panels declared in any child dashboard. Result: the parent shows only metrics its children don't already plot, eliminating redundant rendering of the same series in two dashboards. - config/pumpingStation.json: 11 non-row panels annotated with meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2), Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits). - src/specificClass.js: generateDashboardsForGraph runs the parent-panel filter after composing children; row panels always kept; panels without emittedFields declaration always kept (no silent removal). - test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation presence, child-covered removal, no-overlap preservation, row preservation. Closes #39
This commit is contained in:
102
test/basic/slice39-no-duplication.basic.test.js
Normal file
102
test/basic/slice39-no-duplication.basic.test.js
Normal file
@@ -0,0 +1,102 @@
|
||||
'use strict';
|
||||
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const DashboardApi = require('../../src/specificClass.js');
|
||||
|
||||
function makeChild(id, softwareType) {
|
||||
return {
|
||||
config: {
|
||||
general: { id, name: id },
|
||||
functionality: { softwareType, positionVsParent: 'downstream' },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function makeRoot(softwareType, children) {
|
||||
const map = new Map();
|
||||
for (const c of children) {
|
||||
map.set(c.config.general.id, {
|
||||
child: c,
|
||||
softwareType: c.config.functionality.softwareType,
|
||||
position: 'downstream',
|
||||
});
|
||||
}
|
||||
return {
|
||||
config: {
|
||||
general: { id: 'root-1', name: 'PS-North' },
|
||||
functionality: { softwareType, positionVsParent: 'atequipment' },
|
||||
},
|
||||
childRegistrationUtils: { registeredChildren: map },
|
||||
};
|
||||
}
|
||||
|
||||
test('pumpingStation template has emittedFields on every non-row panel', () => {
|
||||
const api = new DashboardApi({});
|
||||
const dash = api.loadTemplate('pumpingStation');
|
||||
const annotated = dash.panels.filter((p) => p.type !== 'row' && p?.meta?.emittedFields);
|
||||
const nonRowPanels = dash.panels.filter((p) => p.type !== 'row');
|
||||
assert.equal(annotated.length, nonRowPanels.length,
|
||||
`expected all ${nonRowPanels.length} non-row panels annotated, got ${annotated.length}`);
|
||||
});
|
||||
|
||||
test('child-covered fields remove duplicate parent panels', () => {
|
||||
const api = new DashboardApi({});
|
||||
|
||||
// Parent + 1 child with a fake template that emits 'level' (matches one of
|
||||
// the pumpingStation parent's panels). The parent's "Level" panel should
|
||||
// be removed when the child covers it.
|
||||
const child1 = makeChild('child-1', 'measurement');
|
||||
const root = makeRoot('pumpingStation', [child1]);
|
||||
|
||||
// Pre-count parent panels with the 'level' emitted field.
|
||||
const parentTemplate = api.loadTemplate('pumpingStation');
|
||||
const parentLevelPanels = parentTemplate.panels.filter(
|
||||
(p) => p?.meta?.emittedFields?.includes('level')
|
||||
);
|
||||
assert.ok(parentLevelPanels.length > 0, 'parent has level panels in template');
|
||||
|
||||
// Monkey-patch the child's dashboard to claim it covers 'level'.
|
||||
const origLoad = api.loadTemplate.bind(api);
|
||||
api.loadTemplate = function (type) {
|
||||
const dash = origLoad(type);
|
||||
if (type === 'measurement' && dash) {
|
||||
// Inject emittedFields = ['level'] on first non-row panel.
|
||||
const firstPanel = dash.panels.find((p) => p.type !== 'row');
|
||||
if (firstPanel) (firstPanel.meta ||= {}).emittedFields = ['level'];
|
||||
}
|
||||
return dash;
|
||||
};
|
||||
|
||||
const result = api.generateDashboardsForGraph(root);
|
||||
const rootResult = result[0];
|
||||
const rootLevelPanels = rootResult.dashboard.panels.filter(
|
||||
(p) => p?.meta?.emittedFields?.includes('level')
|
||||
);
|
||||
assert.equal(rootLevelPanels.length, 0,
|
||||
'level panel(s) should be removed from parent when child covers them');
|
||||
});
|
||||
|
||||
test('parent panels are kept when no child covers their fields', () => {
|
||||
const api = new DashboardApi({});
|
||||
const child1 = makeChild('child-1', 'measurement'); // measurement.json has no emittedFields
|
||||
const root = makeRoot('pumpingStation', [child1]);
|
||||
const result = api.generateDashboardsForGraph(root);
|
||||
const rootResult = result[0];
|
||||
const beforeTemplate = api.loadTemplate('pumpingStation');
|
||||
const beforeNonRow = beforeTemplate.panels.filter((p) => p.type !== 'row').length;
|
||||
const afterNonRow = rootResult.dashboard.panels.filter((p) => p.type !== 'row').length;
|
||||
assert.equal(afterNonRow, beforeNonRow,
|
||||
'no panels should be removed when no child declares overlapping fields');
|
||||
});
|
||||
|
||||
test('row panels are never removed (structural)', () => {
|
||||
const api = new DashboardApi({});
|
||||
const child1 = makeChild('child-1', 'measurement');
|
||||
const root = makeRoot('pumpingStation', [child1]);
|
||||
const result = api.generateDashboardsForGraph(root);
|
||||
const rootRows = result[0].dashboard.panels.filter((p) => p.type === 'row');
|
||||
const templateRows = api.loadTemplate('pumpingStation').panels.filter((p) => p.type === 'row');
|
||||
assert.equal(rootRows.length, templateRows.length, 'all row panels preserved');
|
||||
});
|
||||
Reference in New Issue
Block a user