fix(energy-pie): represent indeterminate shares and preserve prepared styling
Tests / test (push) Successful in 50s
Tests / test (push) Successful in 50s
This commit is contained in:
@@ -33,6 +33,7 @@ final class EnergiediagrammModulstrukturTest extends TestCase
|
||||
'EinspeisungZaehlerID',
|
||||
'NetzbezugZaehlerID',
|
||||
'VerbrauchZaehlerID',
|
||||
'EnergieanteileBerechenbar',
|
||||
'Standardzeitraum',
|
||||
'FarbeEigenverbrauch',
|
||||
'FarbeAutarkie',
|
||||
@@ -58,6 +59,30 @@ final class EnergiediagrammModulstrukturTest extends TestCase
|
||||
self::assertStringNotContainsString("addEventListener('load'", $html);
|
||||
}
|
||||
|
||||
public function testOriginalEnergyPieBackgroundStaysReadableAndNonInteractive(): void
|
||||
{
|
||||
$html = file_get_contents(__DIR__ . '/../Energiediagramm/module.html');
|
||||
self::assertNotFalse($html);
|
||||
self::assertMatchesRegularExpression('/#energy-app\\s*\\{[^}]*--card-color: #121216;/s', $html);
|
||||
self::assertMatchesRegularExpression('/#energy-app\\s*\\{[^}]*--content-color: #f5f5f7;/s', $html);
|
||||
self::assertMatchesRegularExpression('/#energy-app\\s*\\{[^}]*color-scheme: dark;/s', $html);
|
||||
self::assertMatchesRegularExpression('/#energy-app\\s*\\{[^}]*isolation: isolate;/s', $html);
|
||||
self::assertSame(1, preg_match('/#energy-app::before\\s*\\{([^}]+)\\}/s', $html, $background));
|
||||
foreach ([
|
||||
'inset: 0',
|
||||
'z-index: -1',
|
||||
'radial-gradient(520px 420px at 18% 18%, rgba(99, 179, 255, 0.42), transparent 60%)',
|
||||
'radial-gradient(560px 460px at 82% 28%, rgba(168, 85, 247, 0.38), transparent 62%)',
|
||||
'radial-gradient(620px 520px at 55% 85%, rgba(99, 179, 255, 0.26), transparent 64%)',
|
||||
'filter: blur(18px)',
|
||||
'pointer-events: none',
|
||||
] as $style) {
|
||||
self::assertStringContainsString($style, $background[1]);
|
||||
}
|
||||
self::assertStringContainsString('overflow: auto', $html);
|
||||
self::assertStringNotContainsString('touch-action: none', $html);
|
||||
}
|
||||
|
||||
public function testModuleEmbedsInitialPayloadAndEnablesSupportedVisualization(): void
|
||||
{
|
||||
$module = file_get_contents(__DIR__ . '/../Energiediagramm/module.php');
|
||||
|
||||
@@ -127,4 +127,28 @@ final class EnergiediagrammTest extends TestCase
|
||||
EnergyChartCalculator::normalizeColor('-1', '#22a06b')
|
||||
);
|
||||
}
|
||||
|
||||
public function testMixedMeasurementBoundariesKeepTotalsWithoutInventingShares(): void
|
||||
{
|
||||
$values = EnergyChartCalculator::metrics(10.0, 0.0, 10.0, 5.0, false);
|
||||
|
||||
self::assertSame(10.0, $values['Produktion']);
|
||||
self::assertSame(0.0, $values['Einspeisung']);
|
||||
self::assertSame(10.0, $values['Netzbezug']);
|
||||
self::assertSame(5.0, $values['Hausverbrauch']);
|
||||
self::assertNull($values['Eigenverbrauch']);
|
||||
self::assertNull($values['Eigenverbrauchsquote']);
|
||||
self::assertNull($values['Autarkiegrad']);
|
||||
}
|
||||
|
||||
public function testUnavailableSharesRemainNullWithZeroEnergy(): void
|
||||
{
|
||||
$unavailable = EnergyChartCalculator::metrics(0.0, 0.0, 0.0, null, false);
|
||||
self::assertNull($unavailable['Eigenverbrauchsquote']);
|
||||
self::assertNull($unavailable['Autarkiegrad']);
|
||||
|
||||
$available = EnergyChartCalculator::metrics(0.0, 0.0, 0.0);
|
||||
self::assertSame(0.0, $available['Eigenverbrauchsquote']);
|
||||
self::assertSame(0.0, $available['Autarkiegrad']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const test = require('node:test');
|
||||
const vm = require('node:vm');
|
||||
|
||||
function visualization(initialState) {
|
||||
const elements = new Map();
|
||||
const element = id => {
|
||||
if (!elements.has(id)) {
|
||||
elements.set(id, {
|
||||
innerHTML: '', textContent: '', value: '', hidden: false,
|
||||
addEventListener() {}, classList: { toggle() {} }
|
||||
});
|
||||
}
|
||||
return elements.get(id);
|
||||
};
|
||||
const window = { setTimeout() {} };
|
||||
const document = {
|
||||
getElementById: element,
|
||||
documentElement: { style: {} },
|
||||
addEventListener() {}
|
||||
};
|
||||
const html = fs.readFileSync(path.join(__dirname, '../Energiediagramm/module.html'), 'utf8');
|
||||
const script = html.match(/<script>([\s\S]*?)<\/script>/)[1]
|
||||
.replace('__ENELIX_INITIAL_STATE__', JSON.stringify(initialState));
|
||||
vm.runInNewContext(script, {
|
||||
window, document,
|
||||
getComputedStyle: () => ({ getPropertyValue: () => '' })
|
||||
});
|
||||
return { element, window };
|
||||
}
|
||||
|
||||
function payload(overrides = {}) {
|
||||
return {
|
||||
range: 'day', date: '2026-10-06', hasData: true,
|
||||
values: {
|
||||
Produktion: 10, Hausverbrauch: 5, Netzbezug: 10, Einspeisung: 0,
|
||||
Eigenverbrauchsquote: null, Autarkiegrad: null
|
||||
},
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
test('unavailable shares show neutral rings while preserving energy totals', () => {
|
||||
const ui = visualization(payload({ sharesAvailable: false, attributionHint: 'Different measurement boundaries.' }));
|
||||
for (const id of ['self-consumption-chart', 'autarky-chart']) {
|
||||
const html = ui.element(id).innerHTML;
|
||||
assert.match(html, /Nicht bestimmbar/);
|
||||
assert.doesNotMatch(html, /donut-value|0\.0%/);
|
||||
}
|
||||
assert.match(ui.element('summary').innerHTML, /Produktion/);
|
||||
assert.match(ui.element('summary').innerHTML, /10\.00 kWh/);
|
||||
assert.match(ui.element('summary').innerHTML, /5\.00 kWh/);
|
||||
assert.equal(ui.element('message').hidden, false);
|
||||
assert.equal(ui.element('message').textContent, 'Different measurement boundaries.');
|
||||
});
|
||||
|
||||
test('unavailable flag cannot display stale numeric percentages', () => {
|
||||
const state = payload({ sharesAvailable: false });
|
||||
state.values.Eigenverbrauchsquote = 75;
|
||||
state.values.Autarkiegrad = 50;
|
||||
const ui = visualization(state);
|
||||
assert.doesNotMatch(ui.element('self-consumption-chart').innerHTML, /75\.0%/);
|
||||
assert.doesNotMatch(ui.element('autarky-chart').innerHTML, /50\.0%/);
|
||||
});
|
||||
|
||||
test('legacy numeric percentages and later recovery remain compatible', () => {
|
||||
const ui = visualization(payload({ sharesAvailable: false }));
|
||||
const state = payload();
|
||||
state.values.Eigenverbrauchsquote = 75;
|
||||
state.values.Autarkiegrad = 81.25;
|
||||
ui.window.handleMessage(JSON.stringify(state));
|
||||
assert.match(ui.element('self-consumption-chart').innerHTML, /75\.0%/);
|
||||
assert.match(ui.element('autarky-chart').innerHTML, /81\.3%/);
|
||||
assert.equal(ui.element('message').hidden, true);
|
||||
});
|
||||
|
||||
test('missing archive values do not hide the attribution limitation', () => {
|
||||
const ui = visualization(payload({
|
||||
hasData: false, noDataHint: 'No archive data.',
|
||||
sharesAvailable: false, attributionHint: 'Different measurement boundaries.'
|
||||
}));
|
||||
assert.equal(ui.element('message').textContent,
|
||||
'No archive data. Different measurement boundaries.');
|
||||
});
|
||||
Reference in New Issue
Block a user