M5: Energy view — per-metric decimal formatting and chart auto-refresh toggle

This commit is contained in:
2026-06-22 19:18:29 +02:00
parent 4767a7af60
commit 935e68846c
7 changed files with 386 additions and 26 deletions
+120
View File
@@ -0,0 +1,120 @@
/**
* Tests for energy/format.ts — formatMetricValue helper.
*
* Coverage:
* 1. energy device_class → 3 decimal places.
* 2. non-energy device_class → 2 decimal places.
* 3. null value → "—" placeholder.
* 4. undefined value → "—" placeholder.
* 5. NaN value → "—" placeholder.
* 6. Missing / null metric metadata → default 2 decimal places.
* 7. Integer value still rendered with correct decimal places.
* 8. String numeric value is coerced and formatted.
*/
import { describe, it, expect } from 'vitest'
import { formatMetricValue, VALUE_PLACEHOLDER } from './format'
import type { MetricInfo } from './hooks'
// ---------------------------------------------------------------------------
// Fixtures
// ---------------------------------------------------------------------------
function makeMetric(overrides: Partial<MetricInfo> = {}): MetricInfo {
return {
key: 'voltage',
label: 'Voltage',
unit: 'V',
device_class: 'voltage',
...overrides,
}
}
const energyMetric = makeMetric({ key: 'import_energy', label: 'Import Energy', unit: 'kWh', device_class: 'energy' })
const voltageMetric = makeMetric()
const powerMetric = makeMetric({ key: 'active_power', label: 'Active Power', unit: 'W', device_class: 'power' })
const powerFactorMetric = makeMetric({ key: 'power_factor', label: 'Power Factor', unit: '', device_class: 'power_factor' })
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
describe('formatMetricValue — energy device_class → 3 decimal places', () => {
it('formats kWh energy value to 3 decimal places', () => {
expect(formatMetricValue(123.4567, energyMetric)).toBe('123.457')
})
it('formats a whole-number energy value to 3 decimal places', () => {
expect(formatMetricValue(123, energyMetric)).toBe('123.000')
})
it('formats zero energy value to 3 decimal places', () => {
expect(formatMetricValue(0, energyMetric)).toBe('0.000')
})
})
describe('formatMetricValue — non-energy device_class → 2 decimal places', () => {
it('formats voltage to 2 decimal places', () => {
expect(formatMetricValue(230.2567, voltageMetric)).toBe('230.26')
})
it('formats power to 2 decimal places', () => {
expect(formatMetricValue(295.0, powerMetric)).toBe('295.00')
})
it('formats power factor to 2 decimal places', () => {
expect(formatMetricValue(0.98, powerFactorMetric)).toBe('0.98')
})
it('formats current to 2 decimal places', () => {
const currentMetric = makeMetric({ key: 'current', label: 'Current', unit: 'A', device_class: 'current' })
expect(formatMetricValue(1.3456, currentMetric)).toBe('1.35')
})
})
describe('formatMetricValue — null/undefined/NaN → placeholder', () => {
it('returns placeholder for null', () => {
expect(formatMetricValue(null, voltageMetric)).toBe(VALUE_PLACEHOLDER)
})
it('returns placeholder for undefined', () => {
expect(formatMetricValue(undefined, voltageMetric)).toBe(VALUE_PLACEHOLDER)
})
it('returns placeholder for NaN', () => {
expect(formatMetricValue(NaN, voltageMetric)).toBe(VALUE_PLACEHOLDER)
})
it('returns placeholder for non-numeric string', () => {
expect(formatMetricValue('not-a-number', voltageMetric)).toBe(VALUE_PLACEHOLDER)
})
it('returns placeholder for Infinity', () => {
expect(formatMetricValue(Infinity, voltageMetric)).toBe(VALUE_PLACEHOLDER)
})
})
describe('formatMetricValue — missing metric metadata → default 2 decimal places', () => {
it('falls back to 2 decimal places when metric is undefined', () => {
expect(formatMetricValue(230.2567)).toBe('230.26')
})
it('falls back to 2 decimal places when metric is null', () => {
expect(formatMetricValue(230.2567, null)).toBe('230.26')
})
it('returns placeholder for null value even without metric', () => {
expect(formatMetricValue(null)).toBe(VALUE_PLACEHOLDER)
})
})
describe('formatMetricValue — numeric string coercion', () => {
it('coerces a numeric string to number and formats it', () => {
// API could theoretically return strings; we handle them gracefully.
expect(formatMetricValue('230.2567', voltageMetric)).toBe('230.26')
})
it('coerces a numeric string for energy metric → 3 decimals', () => {
expect(formatMetricValue('123.4567', energyMetric)).toBe('123.457')
})
})