M8-R13: simplify prices and meter controls
frontend / frontend (push) Successful in 53s
pytest / test (push) Successful in 4m34s
docker-image / build-and-push (push) Successful in 1m49s

This commit is contained in:
2026-08-27 20:01:41 +02:00
parent c9ce05d95a
commit b472f91f19
4 changed files with 145 additions and 34 deletions
+44 -3
View File
@@ -209,14 +209,55 @@ describe('MeterManager — meter list', () => {
{ uuid: 'binding-active', source_uuid: 'source-new', source_channel_uuid: 'channel-new', started_at: '2025-02-01T00:00:00Z', ended_at: null },
],
}
mockGet.mockResolvedValue({ data: { items: [meterWithBindings], total: 1 } })
mockGet.mockImplementation((path: string) => {
if (path === '/api/energy/meters') return Promise.resolve({ data: { items: [meterWithBindings], total: 1 } })
if (path === '/api/energy/sources') return Promise.resolve({ data: { items: [
{ uuid: 'source-old', name: 'Old source' }, { uuid: 'source-new', name: 'New source' },
], total: 2 } })
if (path === '/api/energy/sources/{source_uuid}/channels') return Promise.resolve({ data: { items: [
{ uuid: 'channel-old', label: 'Old channel', unit: 'kWh' }, { uuid: 'channel-new', label: 'New channel', unit: 'kWh' },
], total: 2 } })
return Promise.resolve({ data: { items: [] } })
})
renderWithProviders(<MeterManager />)
const closed = await screen.findByTestId('binding-timeline-binding-closed')
await waitFor(() => expect(closed).toHaveTextContent('Old source → Old channel'))
const active = screen.getByTestId('binding-timeline-binding-active')
expect(closed).toHaveTextContent('source-old → channel-old')
expect(closed).toHaveTextContent('Old source → Old channel')
expect(closed).toHaveTextContent('[1/1/2025, 00:00:00, 2/1/2025, 00:00:00) (closed)')
expect(active).toHaveTextContent('source-new → channel-new')
expect(active).toHaveTextContent('New source → New channel')
expect(active).toHaveTextContent('[2/1/2025, 00:00:00, open-ended) (active)')
expect(closed).not.toHaveTextContent('source-old')
expect(active).not.toHaveTextContent('channel-new')
})
it('uses UUID-free honest binding fallbacks while source details load or fail', async () => {
const meter = { ...ACTIVE_METER, bindings: [{ uuid: 'binding-visible-id-only', source_uuid: 'private-source-id', source_channel_uuid: 'private-channel-id', started_at: '2025-01-01T00:00:00Z', ended_at: null }] }
mockGet.mockImplementation((path: string) => {
if (path === '/api/energy/meters') return Promise.resolve({ data: { items: [meter], total: 1 } })
if (path === '/api/energy/sources') return new Promise(() => {})
return Promise.resolve({ data: { items: [] } })
})
renderWithProviders(<MeterManager />)
const timeline = await screen.findByTestId('binding-timeline-binding-visible-id-only')
expect(timeline).toHaveTextContent('Loading source details… → Channel details unavailable')
expect(timeline).not.toHaveTextContent('private-source-id')
expect(timeline).not.toHaveTextContent('private-channel-id')
})
it('keeps meter row actions in the shared light, xs order', async () => {
const binding = { uuid: 'ordered-binding', source_uuid: 'source-1', source_channel_uuid: 'channel-1', started_at: '2024-01-15T00:00:00Z', ended_at: null }
mockGet.mockImplementation((path: string) => {
if (path === '/api/energy/meters') return Promise.resolve({ data: { items: [{ ...ACTIVE_METER, bindings: [binding] }], total: 1 } })
if (path === '/api/energy/sources') return Promise.resolve({ data: { items: [{ uuid: 'source-1', name: 'DSMR' }], total: 1 } })
if (path === '/api/energy/sources/{source_uuid}/channels') return Promise.resolve({ data: { items: [{ uuid: 'channel-1', label: 'Total import', unit: 'kWh' }], total: 1 } })
return Promise.resolve({ data: { items: [] } })
})
renderWithProviders(<MeterManager />)
const row = await screen.findByTestId(`meter-row-${ACTIVE_METER.id}`)
const buttons = Array.from(row.querySelectorAll('button'))
expect(buttons.map((button) => button.textContent)).toEqual(['Edit', 'Transfer source', 'Unbind', 'Close meter'])
expect(buttons).toHaveLength(4)
})
it('renders "Declare New Meter" button', async () => {