/**
* Tests for the Failure Detection settings component (#172).
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { render } from '../utils';
import { FailureDetectionSettings } from '../../components/FailureDetectionSettings';
import { http, HttpResponse } from 'msw';
import { server } from '../mocks/server';
const baseSettings = {
auto_archive: true,
save_thumbnails: true,
capture_finish_photo: true,
default_filament_cost: 25,
currency: 'USD',
energy_cost_per_kwh: 0.15,
energy_tracking_mode: 'total',
check_updates: true,
check_printer_firmware: true,
include_beta_updates: false,
obico_enabled: false,
obico_ml_url: '',
obico_sensitivity: 'medium',
obico_action: 'notify',
obico_poll_interval: 10,
obico_enabled_printers: '',
};
const baseStatus = {
is_running: true,
last_error: null,
per_printer: {},
thresholds: { low: 0.38, high: 0.78 },
history: [],
enabled: false,
ml_url: '',
sensitivity: 'medium',
action: 'notify',
poll_interval: 10,
};
describe('FailureDetectionSettings', () => {
beforeEach(() => {
vi.clearAllMocks();
server.use(
http.get('/api/v1/settings/', () => HttpResponse.json(baseSettings)),
http.get('/api/v1/obico/status', () => HttpResponse.json(baseStatus)),
http.get('/api/v1/printers', () => HttpResponse.json([])),
);
});
it('renders headings and fields', async () => {
render();
await waitFor(() => {
expect(screen.getByText(/AI Failure Detection|Failure Detection/i)).toBeInTheDocument();
});
expect(screen.getByText(/Obico ML API URL/i)).toBeInTheDocument();
expect(screen.getByText(/Sensitivity/i)).toBeInTheDocument();
});
it('test button calls the test-connection endpoint and shows success', async () => {
let called = false;
server.use(
http.get('/api/v1/settings/', () =>
HttpResponse.json({ ...baseSettings, obico_enabled: true, obico_ml_url: 'http://obico:3333' }),
),
http.post('/api/v1/obico/test-connection', async ({ request }) => {
called = true;
const body = (await request.json()) as { url: string };
expect(body.url).toBe('http://obico:3333');
return HttpResponse.json({ ok: true, status_code: 200, body: 'ok', error: null });
}),
);
render();
const testBtn = await screen.findByRole('button', { name: /test/i });
await userEvent.click(testBtn);
await waitFor(() => {
expect(called).toBe(true);
});
expect(await screen.findByText(/ML API reachable/i)).toBeInTheDocument();
});
it('shows failure class history entries with red styling', async () => {
server.use(
http.get('/api/v1/obico/status', () =>
HttpResponse.json({
...baseStatus,
history: [
{
printer_id: 1,
task_name: 'test.3mf',
timestamp: '2026-04-13T10:00:00Z',
current_p: 0.9,
score: 0.85,
class: 'failure',
detections: 1,
},
],
}),
),
);
render();
// Match the history row's score-and-class text, which looks like "failure 0.850"
expect(await screen.findByText(/failure\s+0\.850/)).toBeInTheDocument();
});
});