M8-T18: add source and multi-commodity meter UI
This commit is contained in:
@@ -84,6 +84,7 @@ const METERS_RESPONSE = {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('MeterManager — loading / error / empty states', () => {
|
||||
// M8 keeps the existing Modbus-facing meter regressions alongside commodity additions.
|
||||
beforeEach(() => vi.clearAllMocks())
|
||||
|
||||
it('renders loading state initially', () => {
|
||||
@@ -115,6 +116,24 @@ describe('MeterManager — loading / error / empty states', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('MeterManager — binding switch safety', () => {
|
||||
beforeEach(() => vi.clearAllMocks())
|
||||
it('does not offer switching for a closed meter epoch', async () => {
|
||||
mockGet.mockResolvedValue({ data: { items: [CLOSED_METER], total: 1 } })
|
||||
renderWithProviders(<MeterManager />)
|
||||
await waitFor(() => expect(screen.getByTestId('meters-table')).toBeInTheDocument())
|
||||
expect(screen.queryByRole('button', { name: 'Switch source' })).not.toBeInTheDocument()
|
||||
})
|
||||
it('disables switch submit until the binding timeline has loaded', async () => {
|
||||
const user = userEvent.setup()
|
||||
mockGet.mockImplementation((path: string) => path === '/api/energy/meters' ? Promise.resolve({ data: { items: [ACTIVE_METER], total: 1 } }) : new Promise(() => {}))
|
||||
renderWithProviders(<MeterManager />)
|
||||
await user.click(await screen.findByRole('button', { name: 'Switch source' }))
|
||||
expect(await screen.findByText('Loading binding timeline…')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Switch binding' })).toBeDisabled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('MeterManager — meter list', () => {
|
||||
beforeEach(() => vi.clearAllMocks())
|
||||
|
||||
@@ -140,6 +159,24 @@ describe('MeterManager — meter list', () => {
|
||||
expect(screen.getByText('meter_swap')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders every binding timeline segment with source, channel, and half-open boundaries', async () => {
|
||||
const meterWithBindings = {
|
||||
...ACTIVE_METER,
|
||||
bindings: [
|
||||
{ uuid: 'binding-closed', source_uuid: 'source-old', source_channel_uuid: 'channel-old', started_at: '2025-01-01T00:00:00Z', ended_at: '2025-02-01T00:00:00Z' },
|
||||
{ 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 } })
|
||||
renderWithProviders(<MeterManager />)
|
||||
const closed = await screen.findByTestId('binding-timeline-binding-closed')
|
||||
const active = screen.getByTestId('binding-timeline-binding-active')
|
||||
expect(closed).toHaveTextContent('source-old → channel-old')
|
||||
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('[2/1/2025, 00:00:00, open-ended) (active)')
|
||||
})
|
||||
|
||||
it('renders "Declare New Meter" button', async () => {
|
||||
mockGet.mockResolvedValue({ data: METERS_RESPONSE })
|
||||
|
||||
|
||||
Reference in New Issue
Block a user