/** * Tests for Tibber test button in ConfigPage. * * Coverage: * 1. Tibber test button appears when Tibber section is in config. * 2. Success tri-state shows green alert. * 3. Config-error tri-state shows orange alert. * 4. Failed tri-state shows red alert. */ import { describe, it, expect, vi, beforeEach } from 'vitest' import { screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { renderWithProviders } from '../test-utils' // --------------------------------------------------------------------------- // Mock apiClient // --------------------------------------------------------------------------- const mockGet = vi.fn() const mockPost = vi.fn() const mockPut = vi.fn() vi.mock('../api/client', () => ({ default: { GET: (...args: unknown[]) => mockGet(...args), POST: (...args: unknown[]) => mockPost(...args), PUT: (...args: unknown[]) => mockPut(...args), PATCH: vi.fn(), DELETE: vi.fn(), }, ApiError: class ApiError extends Error { status: number body: unknown constructor(status: number, body: unknown) { super(`API error ${status}`) this.name = 'ApiError' this.status = status this.body = body } }, registerLoginRedirect: vi.fn(), })) // --------------------------------------------------------------------------- // Fixtures // --------------------------------------------------------------------------- const CONFIG_WITH_TIBBER = { sections: [ { name: 'Tibber', fields: [ { env_name: 'TIBBER_TOKEN', label: 'Tibber API Token', secret: true, input_type: 'text', configured: true, value: null, }, ], }, ], } // --------------------------------------------------------------------------- // Import component // --------------------------------------------------------------------------- import { ConfigPage } from './ConfigPage' // --------------------------------------------------------------------------- // Helper to suppress TOTP and Expose Settings sub-queries // --------------------------------------------------------------------------- function mockConfigDependencies() { mockGet.mockImplementation((path: string) => { if (path === '/api/config') { return Promise.resolve({ data: CONFIG_WITH_TIBBER }) } if (path === '/api/expose') { return Promise.resolve({ data: { catalog: [], mqtt_status: { connected: false, broker: null }, }, }) } if (path === '/api/auth/totp/status') { return Promise.resolve({ data: { enabled: false } }) } return Promise.resolve({ data: null }) }) } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- describe('ConfigPage — Tibber test button', () => { beforeEach(() => vi.clearAllMocks()) it('renders Tibber test button when Tibber section is present', async () => { mockConfigDependencies() renderWithProviders() await waitFor(() => { expect(screen.getByTestId('tibber-test-button')).toBeInTheDocument() }) }) it('shows success alert on successful Tibber test', async () => { const user = userEvent.setup() mockConfigDependencies() mockPost.mockImplementation((path: string) => { if (path === '/api/energy/tibber/test') { return Promise.resolve({ data: { result: 'success', message: 'Connected to Tibber API', price: { starts_at: '2026-06-22T10:00:00Z', total: 0.1337, energy: 0.08, tax: 0.0537, currency: 'EUR', level: 'NORMAL', }, }, }) } return Promise.resolve({ data: null }) }) renderWithProviders() await waitFor(() => { expect(screen.getByTestId('tibber-test-button')).toBeInTheDocument() }) await user.click(screen.getByTestId('tibber-test-button')) await waitFor(() => { expect(screen.getByTestId('tibber-result-success')).toBeInTheDocument() }) expect(screen.getByTestId('tibber-result-success')).toHaveTextContent( 'Tibber connection successful', ) }) it('shows config-error alert when Tibber is misconfigured', async () => { const user = userEvent.setup() mockConfigDependencies() // eslint-disable-next-line @typescript-eslint/no-explicit-any const ApiErrorClass = (await import('../api/client')).ApiError as any mockPost.mockImplementation((path: string) => { if (path === '/api/energy/tibber/test') { throw new ApiErrorClass(400, { result: 'config-error', message: 'Tibber token not configured', }) } return Promise.resolve({ data: null }) }) renderWithProviders() await waitFor(() => { expect(screen.getByTestId('tibber-test-button')).toBeInTheDocument() }) await user.click(screen.getByTestId('tibber-test-button')) await waitFor(() => { expect(screen.getByTestId('tibber-result-config-error')).toBeInTheDocument() }) }) it('shows failed alert when Tibber test fails', async () => { const user = userEvent.setup() mockConfigDependencies() // eslint-disable-next-line @typescript-eslint/no-explicit-any const ApiErrorClass = (await import('../api/client')).ApiError as any mockPost.mockImplementation((path: string) => { if (path === '/api/energy/tibber/test') { throw new ApiErrorClass(500, { result: 'failed', message: 'Connection refused', }) } return Promise.resolve({ data: null }) }) renderWithProviders() await waitFor(() => { expect(screen.getByTestId('tibber-test-button')).toBeInTheDocument() }) await user.click(screen.getByTestId('tibber-test-button')) await waitFor(() => { expect(screen.getByTestId('tibber-result-failed')).toBeInTheDocument() }) }) })