Files
dashboard-cpsp/components/__tests__/SettingsPage.test.tsx
T

291 lines
8.2 KiB
TypeScript

import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import SettingsPage from '../SettingsPage.tsx';
const { createKandangMock, createFlockMock, updateFlockMock, createCycleMock, updateCycleMock, reloadMock, farmState } = vi.hoisted(
() => ({
createKandangMock: vi.fn(),
createFlockMock: vi.fn(),
updateFlockMock: vi.fn(),
createCycleMock: vi.fn(),
updateCycleMock: vi.fn(),
reloadMock: vi.fn().mockResolvedValue(undefined),
farmState: {
sites: [{ id: 'site-a', site_name: 'Site A', user: 99, created_at: '', updated_at: '' }],
kandangs: [] as Array<{
id: number;
kandang_name: string;
site: string;
created_at: string;
updated_at: string;
}>,
flocks: [] as Array<{
id: number;
flock_name: string;
iot_api_url?: string;
kandang: number;
created_at: string;
updated_at: string;
}>,
cycles: [] as unknown[],
selectedSiteId: 'site-a',
selectedKandangId: null as number | null,
selectedCycleId: null as number | null,
selectedCycle: null as unknown,
},
})
);
vi.mock('../../services/apiClient.ts', () => ({
api: {
sites: {
create: vi.fn(),
remove: vi.fn(),
},
kandangs: {
create: createKandangMock,
remove: vi.fn(),
},
flocks: {
create: createFlockMock,
update: updateFlockMock,
remove: vi.fn(),
},
cycles: {
create: createCycleMock,
update: updateCycleMock,
remove: vi.fn(),
},
},
errorMessage: (err: unknown) => (err instanceof Error ? err.message : 'Terjadi kesalahan'),
}));
vi.mock('../../context/AuthContext.tsx', () => ({
useAuth: () => ({
user: {
id: 99,
user_name: 'tester',
display_name: 'Tester',
status: 'superadmin',
created_at: '',
updated_at: '',
},
}),
}));
vi.mock('../../context/FarmContext.tsx', () => ({
useFarm: () => ({
sites: farmState.sites,
kandangs: farmState.kandangs,
flocks: farmState.flocks,
cycles: farmState.cycles,
selectedSiteId: farmState.selectedSiteId,
selectedKandangId: farmState.selectedKandangId,
selectedCycleId: farmState.selectedCycleId,
selectedCycle: farmState.selectedCycle,
reload: reloadMock,
setSelectedSiteId: vi.fn(),
setSelectedKandangId: vi.fn(),
setSelectedCycleId: vi.fn(),
}),
}));
describe('SettingsPage settings forms', () => {
beforeEach(() => {
createKandangMock.mockReset();
createFlockMock.mockReset();
updateFlockMock.mockReset();
createCycleMock.mockReset();
updateCycleMock.mockReset();
reloadMock.mockClear();
farmState.kandangs = [];
farmState.flocks = [];
farmState.cycles = [];
farmState.selectedSiteId = 'site-a';
farmState.selectedKandangId = null;
farmState.selectedCycleId = null;
farmState.selectedCycle = null;
createKandangMock.mockResolvedValue({
id: 11,
kandang_name: 'Kandang 1',
site: 'site-a',
created_at: '',
updated_at: '',
});
createFlockMock.mockResolvedValue({
id: 3,
flock_name: 'Lantai 1',
iot_api_url: '',
kandang: 11,
created_at: '',
updated_at: '',
});
updateFlockMock.mockResolvedValue({
id: 3,
flock_name: 'Lantai 1',
iot_api_url: 'https://example.test/iot/lantai-1',
kandang: 11,
created_at: '',
updated_at: '',
});
createCycleMock.mockResolvedValue({
id: 21,
kandang: 11,
total_days: 35,
current_day: 1,
start_date: '2026-08-18',
end_date: '2026-09-21',
chick_in_weight: 35,
doc_in_count: 20000,
status: 'active',
created_at: '',
updated_at: '',
});
updateCycleMock.mockResolvedValue({
id: 22,
kandang: 11,
total_days: 40,
current_day: 1,
start_date: '2026-08-01',
end_date: '2026-09-09',
chick_in_weight: 38,
doc_in_count: 18000,
status: 'closed',
created_at: '',
updated_at: '',
});
});
it('submits only backend-supported kandang fields', async () => {
render(<SettingsPage />);
fireEvent.change(screen.getByPlaceholderText('Nama kandang'), {
target: { value: 'Kandang 1' },
});
fireEvent.click(screen.getByRole('button', { name: 'Tambah kandang' }));
await waitFor(() => expect(createKandangMock).toHaveBeenCalledTimes(1));
expect(createKandangMock).toHaveBeenCalledWith({
kandang_name: 'Kandang 1',
site: 'site-a',
});
});
it('submits flock fields for the selected kandang', async () => {
farmState.kandangs = [
{ id: 11, kandang_name: 'Kandang 1', site: 'site-a', created_at: '', updated_at: '' },
];
farmState.selectedKandangId = 11;
render(<SettingsPage />);
fireEvent.change(screen.getByPlaceholderText('Nama flock'), {
target: { value: 'Lantai 1' },
});
fireEvent.click(screen.getByRole('button', { name: 'Tambah flock' }));
await waitFor(() => expect(createFlockMock).toHaveBeenCalledTimes(1));
expect(createFlockMock).toHaveBeenCalledWith({
flock_name: 'Lantai 1',
kandang: 11,
});
});
it('saves flock IoT API URL for a listed flock', async () => {
farmState.kandangs = [
{ id: 11, kandang_name: 'Kandang 1', site: 'site-a', created_at: '', updated_at: '' },
];
farmState.selectedKandangId = 11;
farmState.flocks = [
{
id: 3,
flock_name: 'Lantai 1',
iot_api_url: '',
kandang: 11,
created_at: '',
updated_at: '',
},
];
render(<SettingsPage />);
fireEvent.change(screen.getByPlaceholderText('https://…/api/iot/…'), {
target: { value: 'https://example.test/iot/lantai-1' },
});
fireEvent.click(screen.getByRole('button', { name: 'Simpan URL IoT' }));
await waitFor(() => expect(updateFlockMock).toHaveBeenCalledTimes(1));
expect(updateFlockMock).toHaveBeenCalledWith(3, {
iot_api_url: 'https://example.test/iot/lantai-1',
});
});
it('submits cycle fields without flock assignment', async () => {
farmState.kandangs = [
{ id: 11, kandang_name: 'Kandang 1', site: 'site-a', created_at: '', updated_at: '' },
];
farmState.selectedKandangId = 11;
render(<SettingsPage />);
expect(screen.queryByLabelText(/^Siklus$/i)).not.toBeInTheDocument();
expect(screen.queryByLabelText(/Total hari/i)).not.toBeInTheDocument();
expect(screen.queryByLabelText(/Selesai/i)).not.toBeInTheDocument();
fireEvent.change(screen.getByLabelText(/Mulai/i), { target: { value: '2026-08-01' } });
fireEvent.click(screen.getByRole('button', { name: 'Tambah siklus' }));
await waitFor(() => expect(createCycleMock).toHaveBeenCalledTimes(1));
expect(createCycleMock).toHaveBeenCalledWith({
kandang: 11,
total_days: 1,
current_day: 1,
start_date: '2026-08-01',
chick_in_weight: 35,
doc_in_count: 20000,
});
});
it('updates an existing cycle from the edit form', async () => {
farmState.kandangs = [
{ id: 11, kandang_name: 'Kandang 1', site: 'site-a', created_at: '', updated_at: '' },
];
farmState.selectedKandangId = 11;
farmState.cycles = [
{
id: 22,
kandang: 11,
kandang_name: 'Kandang 1',
total_days: 35,
current_day: 1,
visible_through_date: '2026-08-18',
start_date: '2026-08-01',
end_date: null,
chick_in_weight: 35,
doc_in_count: 20000,
feed_initial_balance: 0,
status: 'active',
created_at: '',
updated_at: '',
},
];
render(<SettingsPage />);
fireEvent.click(screen.getByRole('button', { name: 'Ubah' }));
fireEvent.change(screen.getByLabelText(/DOC in/i), { target: { value: '18000' } });
fireEvent.change(screen.getByLabelText(/Berat DOC/i), { target: { value: '38' } });
fireEvent.click(screen.getByRole('button', { name: 'Simpan perubahan' }));
await waitFor(() => expect(updateCycleMock).toHaveBeenCalledTimes(1));
expect(updateCycleMock).toHaveBeenCalledWith(22, {
kandang: 11,
total_days: 1,
start_date: '2026-08-01',
chick_in_weight: 38,
doc_in_count: 18000,
});
});
});