first commit

This commit is contained in:
Alberto-Audrix committed 2026-08-26 13:46:05 +07:00
commit 367533c02b
171 files changed
+101175

No files matched your search

@@ -0,0 +1,195 @@
import { render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import FeedSackCountingPage from '../FeedSackCountingPage.tsx';
const { karungRows, manualRows, cycle } = vi.hoisted(() => {
const cycle = {
id: 1,
kandang: 1,
flock: 1,
total_days: 35,
current_day: 2,
start_date: '2026-08-10',
end_date: '2026-09-13',
chick_in_weight: 45,
doc_in_count: 20000,
status: 'active',
created_at: '',
updated_at: '',
};
const karungRows = [
{
id: 1,
cycle: 1,
kandang: 1,
flock_id: 1,
date: '2026-08-10',
in_today: 10,
out_today: 4,
in_total: 10,
out_total: 4,
feed_use_today: 4,
feed_use_total: 4,
created_at: '',
updated_at: '',
},
{
id: 2,
cycle: 1,
kandang: 1,
flock_id: 1,
date: '2026-08-11',
in_today: 12,
out_today: 6,
in_total: 22,
out_total: 10,
feed_use_today: 6,
feed_use_total: 10,
created_at: '',
updated_at: '',
},
];
const manualRows = [
{
id: 1,
cycle: 1,
kandang: 1,
flock_id: 1,
date: '2026-08-10',
age_manual: 0,
mortality_manual: 0,
mortality_manual_total: 0,
feed_in_manual: 10,
feed_use_manual: 4,
feed_in_manual_total: 10,
feed_use_manual_total: 4,
feed_out_manual: 0,
feed_out_manual_total: 0,
harvest_manual: 0,
harvest_manual_total: 0,
harvest_weight_manual: 0,
harvest_weight_manual_total: 0,
manual_weight: 0,
average_harvest_day_manual: 0,
average_harvest_weight_manual: 0,
fcr_manual: 0,
eef_manual: 0,
created_at: '',
updated_at: '',
},
{
id: 2,
cycle: 1,
kandang: 1,
flock_id: 1,
date: '2026-08-11',
age_manual: 1,
mortality_manual: 0,
mortality_manual_total: 0,
feed_in_manual: 10,
feed_use_manual: 7,
feed_in_manual_total: 20,
feed_use_manual_total: 11,
feed_out_manual: 0,
feed_out_manual_total: 0,
harvest_manual: 0,
harvest_manual_total: 0,
harvest_weight_manual: 0,
harvest_weight_manual_total: 0,
manual_weight: 0,
average_harvest_day_manual: 0,
average_harvest_weight_manual: 0,
fcr_manual: 0,
eef_manual: 0,
created_at: '',
updated_at: '',
},
];
return { karungRows, manualRows, cycle };
});
vi.mock('../../services/apiClient.ts', () => ({
api: {
karungs: {
list: vi.fn().mockResolvedValue(karungRows),
request: vi.fn().mockResolvedValue({ karung: {}, upstream: {} }),
},
insights: {
list: vi.fn().mockResolvedValue([]),
},
manualInputs: {
list: vi.fn().mockResolvedValue(manualRows),
},
},
errorMessage: (err: unknown) => (err instanceof Error ? err.message : 'Terjadi kesalahan'),
}));
vi.mock('../../context/FarmContext.tsx', () => ({
useFarm: () => ({
selectedSite: { id: 1, site_name: 'Sukawarna', user: 1, created_at: '', updated_at: '' },
selectedKandang: {
id: 1,
kandang_name: 'Kandang 1',
site: 1,
created_at: '',
updated_at: '',
},
selectedCycle: cycle,
cycles: [cycle],
setSelectedCycleId: vi.fn(),
}),
}));
class ResizeObserverStub {
observe() {}
unobserve() {}
disconnect() {}
}
describe('FeedSackCountingPage', () => {
it('renders the recreated hitung karung view with stats, saldo, and daily table', async () => {
vi.stubGlobal('ResizeObserver', ResizeObserverStub);
try {
render(<FeedSackCountingPage onSettings={() => undefined} />);
expect(await screen.findByRole('heading', { name: 'Hitung Karung' })).toBeInTheDocument();
expect(
screen.getByText('Monitoring karung masuk dan karung dituang per hari')
).toBeInTheDocument();
expect(screen.getByText(/10 Agu 2026 - 13 Sep 2026/i)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /Sync hari ini/i })).toBeInTheDocument();
// Ringkasan stats
expect(screen.getAllByText('Karung Masuk').length).toBeGreaterThan(0);
expect(screen.getAllByText('Karung Dituang').length).toBeGreaterThan(0);
expect(screen.getAllByText('Karung Keluar').length).toBeGreaterThan(0);
expect(screen.getAllByText('12').length).toBeGreaterThan(0); // in_today dari snapshot terbaru
// Saldo card
expect(screen.getByRole('heading', { name: 'Saldo Karung IOT' })).toBeInTheDocument();
// Trend chart
expect(screen.getByRole('heading', { name: 'Tren Harian Karung IOT' })).toBeInTheDocument();
// Daily table (separate masuk & tuang boxes, each with Manual/IOT/Akurasi)
expect(
screen.getByRole('heading', { name: 'Rincian Harian Karung IOT' })
).toBeInTheDocument();
expect(screen.getAllByText('KARUNG MASUK').length).toBeGreaterThan(0);
expect(screen.getAllByText('KARUNG TUANG').length).toBeGreaterThan(0);
// Headers appear in both boxes
expect(screen.getAllByText('Manual').length).toBeGreaterThanOrEqual(2);
expect(screen.getAllByText('IOT').length).toBeGreaterThanOrEqual(2);
expect(screen.getAllByText('Akurasi').length).toBeGreaterThanOrEqual(2);
// Total columns still show snapshot totals
expect(screen.getAllByText('22').length).toBeGreaterThan(0);
expect(screen.getAllByText('10').length).toBeGreaterThan(0);
// Accuracy: hari ke-2 masuk manual 10 vs IOT 12 -> 83.3%
expect(screen.getAllByText('83.3%').length).toBeGreaterThan(0);
// Hari ke-1 matching values -> 100.0%
expect(screen.getAllByText('100.0%').length).toBeGreaterThan(0);
} finally {
vi.unstubAllGlobals();
}
});
});
+145
View File
@@ -0,0 +1,145 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import SettingsPage from '../SettingsPage.tsx';
const { createKandangMock, createCycleMock, reloadMock, farmState } = vi.hoisted(() => ({
createKandangMock: vi.fn(),
createCycleMock: vi.fn(),
reloadMock: vi.fn().mockResolvedValue(undefined),
farmState: {
sites: [{ id: 7, site_name: 'Site A', user: 99, created_at: '', updated_at: '' }],
kandangs: [] as Array<{
id: number;
kandang_name: string;
site: number;
created_at: string;
updated_at: string;
}>,
cycles: [] as unknown[],
selectedSiteId: 7,
selectedKandangId: 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(),
},
cycles: {
create: createCycleMock,
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',
status: 'superadmin',
created_at: '',
updated_at: '',
},
}),
}));
vi.mock('../../context/FarmContext.tsx', () => ({
useFarm: () => ({
sites: farmState.sites,
kandangs: farmState.kandangs,
cycles: farmState.cycles,
selectedSiteId: farmState.selectedSiteId,
selectedKandangId: farmState.selectedKandangId,
selectedCycle: farmState.selectedCycle,
reload: reloadMock,
setSelectedSiteId: vi.fn(),
setSelectedKandangId: vi.fn(),
setSelectedCycleId: vi.fn(),
}),
}));
describe('SettingsPage settings forms', () => {
beforeEach(() => {
createKandangMock.mockReset();
createCycleMock.mockReset();
reloadMock.mockClear();
farmState.kandangs = [];
farmState.cycles = [];
farmState.selectedSiteId = 7;
farmState.selectedKandangId = null;
farmState.selectedCycle = null;
createKandangMock.mockResolvedValue({
id: 11,
kandang_name: 'Kandang 1',
site: 7,
created_at: '',
updated_at: '',
});
createCycleMock.mockResolvedValue({
id: 21,
kandang: 11,
flock: 3,
total_days: 35,
current_day: 1,
start_date: '2026-08-18',
end_date: '2026-09-21',
chick_in_weight: 45,
doc_in_count: 20000,
status: 'active',
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: 7,
});
});
it('submits cycle fields expected by the backend contract', async () => {
farmState.kandangs = [
{ id: 11, kandang_name: 'Kandang 1', site: 7, created_at: '', updated_at: '' },
];
farmState.selectedKandangId = 11;
render(<SettingsPage />);
expect(screen.queryByLabelText(/Total hari/i)).not.toBeInTheDocument();
fireEvent.change(screen.getByLabelText(/Mulai/i), { target: { value: '2026-08-01' } });
fireEvent.change(screen.getByLabelText(/Selesai/i), { target: { value: '2026-09-04' } });
fireEvent.click(screen.getByRole('button', { name: 'Tambah siklus' }));
await waitFor(() => expect(createCycleMock).toHaveBeenCalledTimes(1));
expect(createCycleMock).toHaveBeenCalledWith({
kandang: 11,
total_days: 35,
current_day: 1,
start_date: '2026-08-01',
end_date: '2026-09-04',
chick_in_weight: 45,
doc_in_count: 20000,
status: 'active',
});
});
});