Files
dashboard-cpsp/components/dashboard/__tests__/DashboardCloseCycleControls.test.tsx
T
2026-10-01 13:23:45 +07:00

116 lines
3.9 KiB
TypeScript

import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import DashboardCloseCycleControls from '../DashboardCloseCycleControls.tsx';
const { requestCloseMock, cancelCloseRequestMock, reloadMock, farmState, authState } = vi.hoisted(
() => ({
requestCloseMock: vi.fn().mockResolvedValue({}),
cancelCloseRequestMock: vi.fn().mockResolvedValue({}),
reloadMock: vi.fn().mockResolvedValue(undefined),
farmState: {
selectedCycle: {
id: 5,
start_date: '2026-09-01',
end_date: null,
status: 'active',
proposed_end_date: null,
visible_through_date: '2026-09-10',
} as Record<string, unknown> | null,
},
authState: {
user: {
id: 1,
user_name: 'staff',
display_name: 'Staff',
status: 'active',
},
},
})
);
vi.mock('../../../services/apiClient.ts', () => ({
api: {
cycles: {
requestClose: requestCloseMock,
cancelCloseRequest: cancelCloseRequestMock,
},
},
errorMessage: (err: unknown) => (err instanceof Error ? err.message : 'Terjadi kesalahan'),
}));
vi.mock('../../../context/AuthContext.tsx', () => ({
useAuth: () => authState,
}));
vi.mock('../../../context/FarmContext.tsx', () => ({
useFarm: () => ({
selectedCycle: farmState.selectedCycle,
reload: reloadMock,
}),
}));
describe('DashboardCloseCycleControls', () => {
beforeEach(() => {
requestCloseMock.mockClear();
cancelCloseRequestMock.mockClear();
reloadMock.mockClear();
authState.user.status = 'active';
farmState.selectedCycle = {
id: 5,
start_date: '2026-09-01',
end_date: null,
status: 'active',
proposed_end_date: null,
visible_through_date: '2026-09-10',
};
});
it('shows date + Ajukan tutup for active cycle and requires confirm before API', async () => {
render(<DashboardCloseCycleControls />);
expect(screen.getByRole('button', { name: /Ajukan tutup/i })).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: /Ajukan tutup/i }));
expect(requestCloseMock).not.toHaveBeenCalled();
expect(screen.getByRole('dialog')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: /Ya, ajukan tutup/i }));
await waitFor(() => {
expect(requestCloseMock).toHaveBeenCalledWith(5, {
proposed_end_date: expect.any(String),
});
});
expect(reloadMock).toHaveBeenCalled();
});
it('shows cancel control when pending_close', () => {
farmState.selectedCycle = {
id: 5,
start_date: '2026-09-01',
end_date: null,
status: 'pending_close',
proposed_end_date: '2026-09-15',
visible_through_date: '2026-09-10',
};
render(<DashboardCloseCycleControls />);
expect(screen.queryByRole('button', { name: /Ajukan tutup/i })).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: /Batalkan permintaan/i })).toBeInTheDocument();
});
it('hides controls for non-active users', () => {
authState.user.status = 'superadmin';
render(<DashboardCloseCycleControls />);
expect(screen.queryByRole('button', { name: /Ajukan tutup/i })).not.toBeInTheDocument();
});
it('shows API failure in an alert popup', async () => {
requestCloseMock.mockRejectedValueOnce(
new Error('gagal kirim ke pusat — coba lagi (400: {"detail":"Cycle is already closed."})')
);
render(<DashboardCloseCycleControls />);
fireEvent.click(screen.getByRole('button', { name: /Ajukan tutup/i }));
fireEvent.click(screen.getByRole('button', { name: /Ya, ajukan tutup/i }));
expect(await screen.findByRole('alertdialog')).toBeInTheDocument();
expect(screen.getByText(/Cycle is already closed/i)).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: /^Tutup$/i }));
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument();
});
});