- Replaced raw input fields for pricing with PriceInput component across various forms and modals to ensure consistent formatting and accessibility. - Updated tests to reflect changes in pricing input handling, ensuring values are displayed in toman with proper formatting. - Enhanced accessibility by adding aria-labels and aria-invalid attributes to PriceInput components. - Adjusted UI elements to improve layout and user experience, particularly in forms related to service items and scheduling. - Changed labels from "نمایش در نوبتدهی" to "نمایش در نوبتدهی آنلاین" for clarity.
138 lines
4.9 KiB
TypeScript
138 lines
4.9 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest';
|
||
import { useState } from 'react';
|
||
import { render, screen, fireEvent } from '@testing-library/react';
|
||
import PriceInput from './PriceInput';
|
||
|
||
/** پوشش حالت واقعی: مقدار توسط والد نگه داشته میشود (controlled). */
|
||
function Controlled({ initial = 0, ...rest }: { initial?: number } & Record<string, unknown>) {
|
||
const [value, setValue] = useState(initial);
|
||
return (
|
||
<>
|
||
<PriceInput value={value} onChange={setValue} {...rest} />
|
||
<span data-testid="value">{value}</span>
|
||
</>
|
||
);
|
||
}
|
||
|
||
const input = () => screen.getByRole('textbox') as HTMLInputElement;
|
||
const emitted = () => screen.getByTestId('value').textContent;
|
||
|
||
describe('PriceInput', () => {
|
||
it('رقم فارسی را میپذیرد و عدد لاتین میدهد', () => {
|
||
render(<Controlled />);
|
||
fireEvent.change(input(), { target: { value: '۸۵۰۰۰' } });
|
||
|
||
expect(emitted()).toBe('85000');
|
||
expect(input().value).toBe('۸۵٬۰۰۰');
|
||
});
|
||
|
||
it('رقم عربی را میپذیرد', () => {
|
||
render(<Controlled />);
|
||
fireEvent.change(input(), { target: { value: '٢٥٠' } });
|
||
|
||
expect(emitted()).toBe('250');
|
||
});
|
||
|
||
it('پیست با واحد و جداکننده پاکسازی میشود', () => {
|
||
render(<Controlled />);
|
||
fireEvent.change(input(), { target: { value: '۸۵,۰۰۰ تومان' } });
|
||
|
||
expect(emitted()).toBe('85000');
|
||
});
|
||
|
||
it('صفرِ تایپشده حفظ میشود و به خالی تبدیل نمیشود', () => {
|
||
render(<Controlled />);
|
||
fireEvent.change(input(), { target: { value: '۰' } });
|
||
|
||
expect(input().value).toBe('۰');
|
||
expect(emitted()).toBe('0');
|
||
});
|
||
|
||
it('پاککردن فیلد ⇒ خالی و مقدار صفر', () => {
|
||
render(<Controlled initial={5000} />);
|
||
fireEvent.change(input(), { target: { value: '' } });
|
||
|
||
expect(input().value).toBe('');
|
||
expect(emitted()).toBe('0');
|
||
});
|
||
|
||
it('مقدار اولیه صفر ⇒ فیلد خالی (نه ۰)', () => {
|
||
render(<Controlled initial={0} />);
|
||
expect(input().value).toBe('');
|
||
});
|
||
|
||
it('min حین تایپ اعمال نمیشود ولی روی blur اعمال میشود', () => {
|
||
render(<Controlled min={100} />);
|
||
fireEvent.change(input(), { target: { value: '۵' } });
|
||
expect(emitted()).toBe('5');
|
||
|
||
fireEvent.blur(input());
|
||
expect(emitted()).toBe('100');
|
||
expect(input().value).toBe('۱۰۰');
|
||
});
|
||
|
||
it('max روی blur اعمال میشود', () => {
|
||
render(<Controlled max={1000} />);
|
||
fireEvent.change(input(), { target: { value: '۵۰۰۰' } });
|
||
fireEvent.blur(input());
|
||
|
||
expect(emitted()).toBe('1000');
|
||
});
|
||
|
||
it('فیلد خالی روی blur به min پرت نمیشود', () => {
|
||
render(<Controlled min={100} />);
|
||
fireEvent.blur(input());
|
||
|
||
expect(input().value).toBe('');
|
||
expect(emitted()).toBe('0');
|
||
});
|
||
|
||
it('حروف نامعتبر حذف میشوند و هرگز NaN نمیدهد', () => {
|
||
render(<Controlled />);
|
||
fireEvent.change(input(), { target: { value: 'ابج' } });
|
||
|
||
expect(input().value).toBe('');
|
||
expect(emitted()).toBe('0');
|
||
expect(emitted()).not.toContain('NaN');
|
||
});
|
||
|
||
it('latin ارقام لاتین با کاما نمایش میدهد', () => {
|
||
render(<Controlled latin />);
|
||
fireEvent.change(input(), { target: { value: '۱۲۰۰' } });
|
||
|
||
expect(input().value).toBe('1,200');
|
||
});
|
||
|
||
it('suffix واحد را کنار فیلد نشان میدهد', () => {
|
||
render(<Controlled suffix="تومان" />);
|
||
expect(screen.getByText('تومان')).toBeTruthy();
|
||
});
|
||
|
||
it('تغییر مقدار از بیرون متن فیلد را همگام میکند', () => {
|
||
const { rerender } = render(<PriceInput value={1000} onChange={vi.fn()} />);
|
||
expect(input().value).toBe('۱٬۰۰۰');
|
||
|
||
rerender(<PriceInput value={0} onChange={vi.fn()} />);
|
||
expect(input().value).toBe('');
|
||
});
|
||
|
||
/**
|
||
* برچسبِ کنارِ بعضی فیلدهای مبلغ `<label>` واقعی نیست (span/div)، پس نامِ
|
||
* دسترسیپذیر باید صریح داده شود؛ پیش از این آن فیلدها input خام بودند و
|
||
* `aria-label` خودشان را داشتند.
|
||
*/
|
||
it('ariaLabel نام دسترسیپذیر میسازد', () => {
|
||
render(<PriceInput value={0} onChange={vi.fn()} ariaLabel="قیمت ویزیت" />);
|
||
|
||
expect(screen.getByRole('textbox', { name: 'قیمت ویزیت' })).toBeTruthy();
|
||
});
|
||
|
||
it('ariaInvalid فقط در حالت خطا روی فیلد مینشیند', () => {
|
||
const { rerender } = render(<PriceInput value={0} onChange={vi.fn()} ariaInvalid />);
|
||
expect(input().getAttribute('aria-invalid')).toBe('true');
|
||
|
||
rerender(<PriceInput value={0} onChange={vi.fn()} />);
|
||
expect(input().getAttribute('aria-invalid')).toBeNull();
|
||
});
|
||
});
|