The table had two date columns for one date: "تاریخ" printed the raw 1405-05-13 string in Latin digits, and the column labelled "میلادی" ran the same day through formatDate — which returns Jalali. One date, twice, under a label that lied. It is now a single formatted Jalali column. The closure form used a native <input type="date">: Gregorian, an English mm/dd/yyyy placeholder in an RTL Persian panel, and a white box in dark mode because a native control does not follow the theme. It is the shared Persian picker now. That picker turned out to be a div with an onClick — no role, no tab stop, no accessible name, and its clear button was a span. Since every page that picks a date goes through it, it gained role/tabIndex/Enter-Space, an ariaLabel prop, and a real button for clear. The page passes labels for the year select and both form fields, and the global topbar search got an aria-label, which takes the runtime accessibility probe on this page to clean. useHolidays now returns an error, so a failed request reads as an error instead of an empty year — previously indistinguishable. The page had no test file; it has eight. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
112 lines
4.7 KiB
TypeScript
112 lines
4.7 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() } }));
|
|
vi.mock('../hooks/usePermissions', () => ({ usePermissions: () => ({ can: () => true }) }));
|
|
|
|
const setOverride = { mutate: vi.fn(), isPending: false };
|
|
const removeOverride = { mutate: vi.fn(), isPending: false };
|
|
|
|
let holidays: Array<{ uuid: string; date: number; jalali_date: string; title: string }> = [];
|
|
let overrides: Array<{ uuid: string; date: number; is_working: boolean; note: string | null }> = [];
|
|
let error: string | null = null;
|
|
|
|
vi.mock('../hooks/useResourceCalendar', () => ({
|
|
useHolidays: () => ({ holidays, overrides, loading: false, error, setOverride, removeOverride }),
|
|
}));
|
|
|
|
import HolidaysSettingsPage from './HolidaysSettingsPage';
|
|
|
|
// ۱۴۰۵-۰۵-۱۳ ⇒ ۲۰۲۶-۰۸-۰۴
|
|
const MARTYRDOM = {
|
|
uuid: 'h-1',
|
|
date: Math.floor(new Date(2026, 7, 4).getTime() / 1000),
|
|
jalali_date: '1405-05-13',
|
|
title: 'شهادت',
|
|
};
|
|
|
|
describe('HolidaysSettingsPage', () => {
|
|
beforeEach(() => {
|
|
holidays = [MARTYRDOM];
|
|
overrides = [];
|
|
error = null;
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
/**
|
|
* پیشتر دو ستون تاریخ بود: «تاریخ» رشتهٔ خامِ `1405-05-13` و «میلادی» که با
|
|
* formatDate همان روز را **شمسی** میداد. یک تاریخ، دو بار، با برچسبی که دروغ میگفت.
|
|
*/
|
|
it('یک ستون تاریخ دارد و شمسی است، نه رشتهٔ خام', () => {
|
|
renderWithProviders(<HolidaysSettingsPage />);
|
|
|
|
expect(screen.getByText('۱۴۰۵/۰۵/۱۳')).toBeInTheDocument();
|
|
expect(screen.queryByText('1405-05-13')).not.toBeInTheDocument();
|
|
expect(screen.queryByText('میلادی')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('روز تعطیل را برای این محیط باز میکند', async () => {
|
|
const user = userEvent.setup();
|
|
renderWithProviders(<HolidaysSettingsPage />);
|
|
|
|
await user.click(screen.getByRole('button', { name: 'این روز باز است' }));
|
|
|
|
expect(setOverride.mutate).toHaveBeenCalledWith({ date: MARTYRDOM.date, is_working: true });
|
|
});
|
|
|
|
it('روزِ استثنا خورده را دوباره تعطیل میکند', async () => {
|
|
overrides = [{ uuid: 'o-1', date: MARTYRDOM.date, is_working: true, note: null }];
|
|
const user = userEvent.setup();
|
|
renderWithProviders(<HolidaysSettingsPage />);
|
|
|
|
expect(screen.getByText('باز است')).toBeInTheDocument();
|
|
await user.click(screen.getByRole('button', { name: 'تعطیل کن' }));
|
|
|
|
expect(removeOverride.mutate).toHaveBeenCalledWith('o-1');
|
|
});
|
|
|
|
/** بدون این، درخواستِ شکستخورده مثل «سالِ خالی» دیده میشد. */
|
|
it('خطای سرور را نشان میدهد، نه سالِ خالی', () => {
|
|
error = 'خطای داخلی سرور';
|
|
holidays = [];
|
|
renderWithProviders(<HolidaysSettingsPage />);
|
|
|
|
expect(screen.getByText('خطای داخلی سرور')).toBeInTheDocument();
|
|
});
|
|
|
|
it('سالِ خالی را با ارقام فارسی میگوید', () => {
|
|
holidays = [];
|
|
renderWithProviders(<HolidaysSettingsPage />, { route: '/admin/holidays?year=1404' });
|
|
|
|
expect(screen.getByText('برای سال ۱۴۰۴ تعطیلی ثبت نشده است')).toBeInTheDocument();
|
|
});
|
|
|
|
/** انتخابگر سال و ورودی تاریخ هر دو باید نام دسترسپذیر داشته باشند. */
|
|
it('فیلدها نام دارند', () => {
|
|
renderWithProviders(<HolidaysSettingsPage />);
|
|
|
|
expect(screen.getByRole('button', { name: 'تاریخ تعطیلی' })).toBeInTheDocument();
|
|
expect(screen.getByLabelText('توضیح')).toBeInTheDocument();
|
|
});
|
|
|
|
/** ورودی نیتیو میلادی بود و در دارکمود سفید میماند؛ باید رفته باشد. */
|
|
it('ورودی تاریخ نیتیو ندارد', () => {
|
|
const { container } = renderWithProviders(<HolidaysSettingsPage />);
|
|
|
|
expect(container.querySelector('input[type="date"]')).toBeNull();
|
|
});
|
|
|
|
it('تعطیلی محیط را با تاریخ و توضیح ثبت میکند', async () => {
|
|
const user = userEvent.setup();
|
|
renderWithProviders(<HolidaysSettingsPage />);
|
|
|
|
// بدون تاریخ، ثبت ممکن نیست.
|
|
expect(screen.getByRole('button', { name: 'افزودن' })).toBeDisabled();
|
|
|
|
await user.type(screen.getByLabelText('توضیح'), 'تعطیلی داخلی');
|
|
expect(screen.getByRole('button', { name: 'افزودن' })).toBeDisabled();
|
|
});
|
|
});
|