Files
clinicpro/assets/admin/pages/NationalHolidaysPage.test.tsx
T
hamedandClaude Opus 5 bd4347f9c5 feat(admin): a page for the official holiday calendar
The three admin endpoints shipped without anywhere to call them from, so the
person who is supposed to maintain the national calendar could only do it
with curl or a console command. That is not "the director can register the
year's holidays".

/admin/national-holidays is ROLE_ADMIN only and sits under the System group
in the admin nav. The year lives in the query string, so back and refresh
return to the year being edited.

Editing takes only the title: `date` is the unique key, so moving a holiday
is really deleting one and creating another, and the form says so rather than
silently creating a duplicate. The date field is the shared Jalali picker,
which speaks Gregorian, so the page converts before POSTing the jalali_date
the API expects — and shows the converted value under the field so the user
can see what will be stored.

Deleting warns that the day leaves every clinic's calendar, because it does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 17:14:23 +03:30

93 lines
3.4 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../test/utils';
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }));
const create = { mutate: vi.fn(), isPending: false };
const update = { mutate: vi.fn(), isPending: false };
const remove = { mutate: vi.fn(), isPending: false };
let holidays: Array<{ uuid: string; date: number; jalali_date: string; jalali_year: number; title: string }> = [];
vi.mock('../hooks/useNationalHolidays', () => ({
useNationalHolidays: () => ({ holidays, loading: false, create, update, remove }),
}));
import NationalHolidaysPage from './NationalHolidaysPage';
// ۱۴۰۵-۰۱-۱۳ ⇒ ۲۰۲۶-۰۴-۰۲
const NOWRUZ13 = {
uuid: 'h-1',
date: Math.floor(new Date(2026, 3, 2).getTime() / 1000),
jalali_date: '1405-01-13',
jalali_year: 1405,
title: 'سیزده‌بدر',
};
describe('NationalHolidaysPage', () => {
beforeEach(() => {
holidays = [NOWRUZ13];
vi.clearAllMocks();
});
it('تعطیلات سال را فهرست می‌کند', () => {
renderWithProviders(<NationalHolidaysPage />);
expect(screen.getByText('سیزده‌بدر')).toBeInTheDocument();
});
/** تاریخ کلید یکتاست؛ ویرایش فقط عنوان را عوض می‌کند. */
it('ویرایش، تاریخ را قفل و فقط عنوان را می‌فرستد', async () => {
const user = userEvent.setup();
renderWithProviders(<NationalHolidaysPage />);
await user.click(screen.getByRole('button', { name: 'ویرایش' }));
expect(screen.getByText(/تاریخ تغییر نمی‌کند/)).toBeInTheDocument();
const title = screen.getByLabelText('مناسبت');
await user.clear(title);
await user.type(title, 'روز طبیعت');
await user.click(screen.getByRole('button', { name: 'ذخیره' }));
expect(update.mutate).toHaveBeenCalledWith(
{ uuid: 'h-1', title: 'روز طبیعت' },
expect.anything(),
);
});
it('بدون تاریخ، دکمهٔ ذخیره غیرفعال است', async () => {
const user = userEvent.setup();
renderWithProviders(<NationalHolidaysPage />);
await user.click(screen.getByRole('button', { name: /افزودن تعطیلی/ }));
await user.type(screen.getByLabelText('مناسبت'), 'مناسبت تازه');
expect(screen.getByRole('button', { name: 'ذخیره' })).toBeDisabled();
});
it('حذف، هشدارِ دامنهٔ اثر را می‌دهد', async () => {
const user = userEvent.setup();
renderWithProviders(<NationalHolidaysPage />);
await user.click(screen.getByRole('button', { name: 'حذف' }));
expect(screen.getByText(/از تقویم همهٔ کلینیک‌ها برداشته می‌شود/)).toBeInTheDocument();
});
it('سالِ خالی را صریح می‌گوید', () => {
holidays = [];
renderWithProviders(<NationalHolidaysPage />);
expect(screen.getByText(/تعطیلی رسمی ثبت نشده است/)).toBeInTheDocument();
});
/** سال در URL می‌نشیند تا «بازگشت» و رفرش همان سال را بدهند. */
it('سال را از URL می‌خواند', () => {
renderWithProviders(<NationalHolidaysPage />, { route: '/admin/national-holidays?year=1403' });
expect(screen.getByText('سال ۱۴۰۳')).toBeInTheDocument();
});
});