first commit
This commit is contained in:
commit
b54624be96
226 files changed
+108840
No files matched your search
@@ -0,0 +1,32 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import AccountAdminShell from '../AccountAdminShell.tsx';
|
||||
|
||||
vi.mock('../../context/AuthContext.tsx', () => ({
|
||||
useAuth: () => ({
|
||||
user: {
|
||||
id: 9,
|
||||
user_name: 'account_admin',
|
||||
status: 'account_admin',
|
||||
is_superuser: false,
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
},
|
||||
logout: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('../UserManagementPage.tsx', () => ({
|
||||
default: () => <div>User management content</div>,
|
||||
}));
|
||||
|
||||
describe('AccountAdminShell', () => {
|
||||
it('shows only User Management nav, not ops dashboards', () => {
|
||||
render(<AccountAdminShell />);
|
||||
expect(screen.getByText('Manajemen Akun')).toBeInTheDocument();
|
||||
expect(screen.getByText('User management content')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Dashboard')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Hitung Ayam')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('FCR')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,288 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { api } from '../../services/apiClient.ts';
|
||||
import FeedSackCountingPage from '../FeedSackCountingPage.tsx';
|
||||
|
||||
const { karungRows, manualRows, cycle, mismatchedKarungRows, mismatchedManualRows } = 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: 'gm',
|
||||
feed_initial_balance: 0,
|
||||
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: '',
|
||||
},
|
||||
];
|
||||
// IoT already on the newer day; manual only exists for the previous day.
|
||||
const mismatchedKarungRows = [
|
||||
{
|
||||
id: 10,
|
||||
cycle: 1,
|
||||
kandang: 1,
|
||||
flock_id: 1,
|
||||
date: '2026-09-02',
|
||||
in_today: 192,
|
||||
out_today: 1,
|
||||
in_total: 192,
|
||||
out_total: 1,
|
||||
feed_use_today: 7,
|
||||
feed_use_total: 7,
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
cycle: 1,
|
||||
kandang: 1,
|
||||
flock_id: 1,
|
||||
date: '2026-09-03',
|
||||
in_today: 50,
|
||||
out_today: 0,
|
||||
in_total: 242,
|
||||
out_total: 1,
|
||||
feed_use_today: 4,
|
||||
feed_use_total: 11,
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
},
|
||||
];
|
||||
const mismatchedManualRows = [
|
||||
{
|
||||
id: 10,
|
||||
cycle: 1,
|
||||
kandang: 1,
|
||||
flock_id: 1,
|
||||
date: '2026-09-02',
|
||||
age_manual: 0,
|
||||
mortality_manual: 0,
|
||||
mortality_manual_total: 0,
|
||||
feed_in_manual: 192,
|
||||
feed_use_manual: 7,
|
||||
feed_in_manual_total: 192,
|
||||
feed_use_manual_total: 7,
|
||||
feed_out_manual: 1,
|
||||
feed_out_manual_total: 1,
|
||||
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, mismatchedKarungRows, mismatchedManualRows };
|
||||
});
|
||||
|
||||
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(),
|
||||
updateCycle: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
class ResizeObserverStub {
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
||||
|
||||
describe('FeedSackCountingPage', () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(api.karungs.list).mockResolvedValue(karungRows);
|
||||
vi.mocked(api.manualInputs.list).mockResolvedValue(manualRows);
|
||||
vi.mocked(api.insights.list).mockResolvedValue([]);
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not reuse yesterday manual values on the latest IoT day stats cards', async () => {
|
||||
vi.stubGlobal('ResizeObserver', ResizeObserverStub);
|
||||
vi.mocked(api.karungs.list).mockResolvedValue(mismatchedKarungRows);
|
||||
vi.mocked(api.manualInputs.list).mockResolvedValue(mismatchedManualRows);
|
||||
try {
|
||||
render(<FeedSackCountingPage onSettings={() => undefined} />);
|
||||
|
||||
expect(await screen.findByRole('heading', { name: 'Hitung Karung' })).toBeInTheDocument();
|
||||
// Latest IoT day still shows.
|
||||
expect(screen.getAllByText('50').length).toBeGreaterThan(0);
|
||||
// Summary Manual cards show 0 when the latest IoT day has no matching manual row.
|
||||
expect(screen.getAllByText('0').length).toBeGreaterThanOrEqual(6);
|
||||
// Daily table still keeps the real 2 Sep manual row across masuk/tuang/keluar boxes.
|
||||
expect(screen.getAllByText('192').length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText('3 Sep 2026').length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText('2 Sep 2026').length).toBeGreaterThan(0);
|
||||
} finally {
|
||||
vi.unstubAllGlobals();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,216 @@
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import SettingsPage from '../SettingsPage.tsx';
|
||||
|
||||
const {
|
||||
listMock,
|
||||
createMock,
|
||||
removeMock,
|
||||
usersListMock,
|
||||
buhsMock,
|
||||
pendingMock,
|
||||
requestMock,
|
||||
approveMock,
|
||||
refreshUserMock,
|
||||
authState,
|
||||
} = vi.hoisted(() => ({
|
||||
listMock: vi.fn(),
|
||||
createMock: vi.fn(),
|
||||
removeMock: vi.fn(),
|
||||
usersListMock: vi.fn(),
|
||||
buhsMock: vi.fn(),
|
||||
pendingMock: vi.fn(),
|
||||
requestMock: vi.fn(),
|
||||
approveMock: vi.fn(),
|
||||
refreshUserMock: vi.fn(),
|
||||
authState: {
|
||||
user: {
|
||||
id: 5,
|
||||
user_name: 'gm.user',
|
||||
status: 'gm' as string,
|
||||
managed_by: null as number | null,
|
||||
managed_by_name: null as string | null,
|
||||
managed_by_pending: null as number | null,
|
||||
managed_by_pending_name: null as string | null,
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../services/apiClient.ts', () => ({
|
||||
api: {
|
||||
citySites: {
|
||||
list: listMock,
|
||||
create: createMock,
|
||||
remove: removeMock,
|
||||
},
|
||||
users: {
|
||||
list: usersListMock,
|
||||
listBuhs: buhsMock,
|
||||
listPendingManaged: pendingMock,
|
||||
requestManagedBy: requestMock,
|
||||
cancelManagedByRequest: vi.fn(),
|
||||
approveManagedBy: approveMock,
|
||||
rejectManagedBy: vi.fn(),
|
||||
},
|
||||
},
|
||||
errorMessage: (err: unknown) => (err instanceof Error ? err.message : 'Terjadi kesalahan'),
|
||||
}));
|
||||
|
||||
vi.mock('../../context/AuthContext.tsx', () => ({
|
||||
useAuth: () => ({
|
||||
user: authState.user,
|
||||
refreshUser: refreshUserMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('../../context/FarmContext.tsx', () => ({
|
||||
useFarm: () => ({
|
||||
reload: vi.fn().mockResolvedValue(undefined),
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('SettingsPage city site registry', () => {
|
||||
beforeEach(() => {
|
||||
listMock.mockReset();
|
||||
createMock.mockReset();
|
||||
removeMock.mockReset();
|
||||
usersListMock.mockReset();
|
||||
buhsMock.mockReset();
|
||||
pendingMock.mockReset();
|
||||
requestMock.mockReset();
|
||||
approveMock.mockReset();
|
||||
refreshUserMock.mockReset();
|
||||
authState.user = {
|
||||
id: 5,
|
||||
user_name: 'gm.user',
|
||||
status: 'gm',
|
||||
managed_by: null,
|
||||
managed_by_name: null,
|
||||
managed_by_pending: null,
|
||||
managed_by_pending_name: null,
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
};
|
||||
refreshUserMock.mockResolvedValue(authState.user);
|
||||
listMock.mockResolvedValue([
|
||||
{
|
||||
id: 1,
|
||||
code: 'sukawarna',
|
||||
name: 'Sukawarna',
|
||||
api_base_url: 'https://sukawarna.example/api/v1',
|
||||
has_api_key: true,
|
||||
registered_by: 5,
|
||||
registered_by_name: 'gm.user',
|
||||
is_active: true,
|
||||
last_synced_at: null,
|
||||
last_sync_status: '',
|
||||
last_sync_message: '',
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
},
|
||||
]);
|
||||
createMock.mockResolvedValue({
|
||||
id: 2,
|
||||
code: 'cikupa',
|
||||
name: 'Cikupa',
|
||||
api_base_url: 'https://cikupa.example/api/v1',
|
||||
has_api_key: false,
|
||||
registered_by: 5,
|
||||
registered_by_name: 'gm.user',
|
||||
is_active: true,
|
||||
last_synced_at: null,
|
||||
last_sync_status: '',
|
||||
last_sync_message: '',
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
});
|
||||
buhsMock.mockResolvedValue([
|
||||
{ id: 2, user_name: 'buh', status: 'buh', created_at: '', updated_at: '' },
|
||||
]);
|
||||
usersListMock.mockResolvedValue([
|
||||
{
|
||||
id: 5,
|
||||
user_name: 'gm',
|
||||
status: 'gm',
|
||||
managed_by: 2,
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
},
|
||||
]);
|
||||
pendingMock.mockResolvedValue([]);
|
||||
});
|
||||
|
||||
it('lists registered city sites for GM', async () => {
|
||||
render(<SettingsPage />);
|
||||
|
||||
expect(await screen.findByText('Sukawarna')).toBeInTheDocument();
|
||||
expect(screen.getByText('BUH pengelola')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('submits a new city site to the view list', async () => {
|
||||
render(<SettingsPage />);
|
||||
await screen.findByText('Sukawarna');
|
||||
|
||||
fireEvent.change(screen.getByPlaceholderText('Contoh: Sukawarna'), {
|
||||
target: { value: 'Cikupa' },
|
||||
});
|
||||
fireEvent.change(screen.getByPlaceholderText('https://site.example/api/v1'), {
|
||||
target: { value: 'https://cikupa.example/api/v1' },
|
||||
});
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Tambah ke daftar' }));
|
||||
|
||||
await waitFor(() => expect(createMock).toHaveBeenCalledTimes(1));
|
||||
expect(createMock).toHaveBeenCalledWith({
|
||||
code: 'cikupa',
|
||||
name: 'Cikupa',
|
||||
api_base_url: 'https://cikupa.example/api/v1',
|
||||
is_active: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('lets GM request a BUH', async () => {
|
||||
requestMock.mockResolvedValue({
|
||||
...authState.user,
|
||||
managed_by_pending: 2,
|
||||
managed_by_pending_name: 'buh',
|
||||
});
|
||||
render(<SettingsPage />);
|
||||
await screen.findByText('BUH pengelola');
|
||||
|
||||
fireEvent.change(screen.getByLabelText(/Pilih BUH/i), { target: { value: '2' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Kirim permintaan' }));
|
||||
|
||||
await waitFor(() => expect(requestMock).toHaveBeenCalledWith(2));
|
||||
});
|
||||
|
||||
it('shows pending GM confirmation for BUH', async () => {
|
||||
authState.user = {
|
||||
id: 2,
|
||||
user_name: 'buh',
|
||||
status: 'buh',
|
||||
managed_by: null,
|
||||
managed_by_name: null,
|
||||
managed_by_pending: null,
|
||||
managed_by_pending_name: null,
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
};
|
||||
pendingMock.mockResolvedValue([
|
||||
{
|
||||
id: 5,
|
||||
user_name: 'gm.user',
|
||||
status: 'gm',
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
},
|
||||
]);
|
||||
|
||||
render(<SettingsPage />);
|
||||
|
||||
expect(await screen.findByText('Permintaan GM')).toBeInTheDocument();
|
||||
expect(screen.getByText('gm.user')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Konfirmasi' })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,141 @@
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import UserManagementPage from '../UserManagementPage.tsx';
|
||||
|
||||
const { listMock, createMock, updateMock, setPasswordMock, authState } = vi.hoisted(() => ({
|
||||
listMock: vi.fn(),
|
||||
createMock: vi.fn(),
|
||||
updateMock: vi.fn(),
|
||||
setPasswordMock: vi.fn(),
|
||||
authState: {
|
||||
user: {
|
||||
id: 99,
|
||||
user_name: 'account_admin',
|
||||
status: 'account_admin' as string,
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../services/apiClient.ts', () => ({
|
||||
api: {
|
||||
users: {
|
||||
list: listMock,
|
||||
create: createMock,
|
||||
update: updateMock,
|
||||
setPassword: setPasswordMock,
|
||||
},
|
||||
},
|
||||
errorMessage: (err: unknown) => (err instanceof Error ? err.message : 'Terjadi kesalahan'),
|
||||
}));
|
||||
|
||||
vi.mock('../../context/AuthContext.tsx', () => ({
|
||||
useAuth: () => ({
|
||||
user: authState.user,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('UserManagementPage', () => {
|
||||
beforeEach(() => {
|
||||
listMock.mockReset();
|
||||
createMock.mockReset();
|
||||
updateMock.mockReset();
|
||||
setPasswordMock.mockReset();
|
||||
authState.user = {
|
||||
id: 99,
|
||||
user_name: 'account_admin',
|
||||
status: 'account_admin',
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
};
|
||||
listMock.mockResolvedValue([
|
||||
{
|
||||
id: 1,
|
||||
user_name: 'gm1',
|
||||
status: 'gm',
|
||||
managed_by_name: 'buh1',
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
},
|
||||
{
|
||||
id: 99,
|
||||
user_name: 'account_admin',
|
||||
status: 'account_admin',
|
||||
managed_by_name: null,
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
},
|
||||
]);
|
||||
createMock.mockResolvedValue({
|
||||
id: 2,
|
||||
user_name: 'newgm',
|
||||
status: 'gm',
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
});
|
||||
updateMock.mockResolvedValue({});
|
||||
setPasswordMock.mockResolvedValue({});
|
||||
vi.spyOn(window, 'confirm').mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('renders list from api.users.list', async () => {
|
||||
render(<UserManagementPage />);
|
||||
expect(await screen.findByText('gm1')).toBeInTheDocument();
|
||||
expect(screen.getByText('buh1')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('GM').length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('submits create with password', async () => {
|
||||
render(<UserManagementPage />);
|
||||
await screen.findByText('gm1');
|
||||
|
||||
fireEvent.change(screen.getByLabelText(/Username/i), {
|
||||
target: { value: 'newgm' },
|
||||
});
|
||||
fireEvent.change(screen.getByLabelText(/Password sementara/i), {
|
||||
target: { value: 'temp123' },
|
||||
});
|
||||
fireEvent.click(screen.getByRole('button', { name: /Buat akun/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(createMock).toHaveBeenCalledWith({
|
||||
user_name: 'newgm',
|
||||
status: 'gm',
|
||||
password: 'temp123',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('deactivates user', async () => {
|
||||
render(<UserManagementPage />);
|
||||
await screen.findByText('gm1');
|
||||
fireEvent.click(screen.getByRole('button', { name: /Nonaktifkan/i }));
|
||||
await waitFor(() => {
|
||||
expect(updateMock).toHaveBeenCalledWith(1, { status: 'inactive' });
|
||||
});
|
||||
});
|
||||
|
||||
it('hides deactivate and role change on own row', async () => {
|
||||
render(<UserManagementPage />);
|
||||
expect(await screen.findByText('(Anda)')).toBeInTheDocument();
|
||||
expect(screen.getAllByRole('button', { name: /Nonaktifkan/i })).toHaveLength(1);
|
||||
expect(screen.getAllByRole('button', { name: /Ubah role/i })).toHaveLength(1);
|
||||
expect(
|
||||
screen.getByText(/Role & status akun sendiri tidak bisa diubah/i)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('resets password', async () => {
|
||||
render(<UserManagementPage />);
|
||||
await screen.findByText('gm1');
|
||||
fireEvent.click(screen.getAllByRole('button', { name: /Reset password/i })[0]);
|
||||
fireEvent.change(screen.getByPlaceholderText(/Password baru/i), {
|
||||
target: { value: 'reset-me' },
|
||||
});
|
||||
fireEvent.click(screen.getByRole('button', { name: /^Reset$/i }));
|
||||
await waitFor(() => {
|
||||
expect(setPasswordMock).toHaveBeenCalledWith(1, 'reset-me');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user