321 lines
9.1 KiB
TypeScript
321 lines
9.1 KiB
TypeScript
|
||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||
import { useUpdateUser } from '@/apps/new-ui/services/User';
|
||
import { useGetAllSpecialties } from '@/apps/new-ui/services/Specialty';
|
||
import toast from 'react-hot-toast';
|
||
import EditInformation from '@/apps/new-ui/pages/Profile/+components/EditInformation';
|
||
|
||
// Mock کردن ماژولها
|
||
jest.mock('@/apps/new-ui/services/User');
|
||
jest.mock('@/apps/new-ui/services/Specialty');
|
||
jest.mock('react-hot-toast');
|
||
|
||
jest.mock("react-dom", () => ({
|
||
...jest.requireActual("react-dom"),
|
||
createPortal: (node: any) => node,
|
||
}));
|
||
jest.mock("@/utils/axios-interceptor", () => ({
|
||
appointmentClient: {
|
||
get: jest.fn(),
|
||
post: jest.fn(),
|
||
put: jest.fn(),
|
||
delete: jest.fn(),
|
||
},
|
||
}));
|
||
jest.mock("../src/apps/new-ui/Utils/env.ts", () => ({
|
||
ENV: {
|
||
API_URL: "https://api.test",
|
||
},
|
||
}));
|
||
|
||
jest.mock('@/apps/new-ui/components/MobilePicker/MobileScrollDatePicker', () => ({
|
||
__esModule: true,
|
||
default: ({ value, onChange }: any) => (
|
||
<div data-testid="date-picker">
|
||
<input
|
||
type="date"
|
||
value={value}
|
||
onChange={(e) => onChange(e.target.value)}
|
||
data-testid="date-input"
|
||
/>
|
||
</div>
|
||
),
|
||
}));
|
||
|
||
jest.mock('@/apps/new-ui/components/Inputs/NumericInput', () => ({
|
||
__esModule: true,
|
||
default: ({ value, onChange, placeholder, className, ...props }: any) => (
|
||
<input
|
||
type="text"
|
||
value={value}
|
||
onChange={(e) => onChange(e.target.value)}
|
||
placeholder={placeholder}
|
||
className={className}
|
||
data-testid="numeric-input"
|
||
{...props}
|
||
/>
|
||
),
|
||
}));
|
||
|
||
jest.mock('@/apps/new-ui/components/CustomeComponents/MapPicker', () => ({
|
||
__esModule: true,
|
||
default: ({ latitude, longitude, onChange }: any) => (
|
||
<div data-testid="map-picker">
|
||
<button
|
||
data-testid="map-click"
|
||
onClick={() => onChange(35.7000, 51.4000)}
|
||
>
|
||
Change Location
|
||
</button>
|
||
<span>Lat: {latitude}, Lng: {longitude}</span>
|
||
</div>
|
||
),
|
||
}));
|
||
|
||
// مسیرهای اصلاح شده
|
||
jest.mock('@/apps/new-ui/pages/Profile/+components/EditPhoto', () => ({
|
||
__esModule: true,
|
||
default: ({ onClose }: any) => (
|
||
<div data-testid="edit-photo-modal">
|
||
<button onClick={onClose} data-testid="close-photo-modal">Close</button>
|
||
</div>
|
||
),
|
||
}));
|
||
|
||
jest.mock('@/apps/new-ui/pages/Profile/+components/SelectSpecialization', () => ({
|
||
__esModule: true,
|
||
default: ({ selectedSpecialization, onSpecializationChange, specializations }: any) => (
|
||
<div data-testid="specialization-select">
|
||
<select
|
||
value={selectedSpecialization}
|
||
onChange={(e) => onSpecializationChange(e.target.value)}
|
||
data-testid="specialization-dropdown"
|
||
>
|
||
<option value="">انتخاب تخصص</option>
|
||
{specializations?.map((spec: any) => (
|
||
<option key={spec.id} value={spec.name}>{spec.name}</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
),
|
||
}));
|
||
|
||
jest.mock('@/apps/new-ui/pages/Profile/+components/SelectProvinceCity', () => ({
|
||
__esModule: true,
|
||
default: ({ selectedProvince, selectedCity, onProvinceChange, onCityChange }: any) => (
|
||
<div data-testid="province-city-select">
|
||
<input
|
||
data-testid="province-input"
|
||
value={selectedProvince}
|
||
onChange={(e) => onProvinceChange(e.target.value)}
|
||
placeholder="استان"
|
||
/>
|
||
<input
|
||
data-testid="city-input"
|
||
value={selectedCity}
|
||
onChange={(e) => onCityChange(e.target.value)}
|
||
placeholder="شهر"
|
||
/>
|
||
</div>
|
||
),
|
||
}));
|
||
|
||
// Mock createPortal
|
||
jest.mock('react-dom', () => ({
|
||
...jest.requireActual('react-dom'),
|
||
createPortal: (node: React.ReactNode) => node,
|
||
}));
|
||
|
||
describe('EditInformation', () => {
|
||
const mockOnClick = jest.fn();
|
||
const mockUpdateUser = jest.fn();
|
||
const mockUserInfo = {
|
||
name: 'علی رضایی',
|
||
phone_number: '09123456789',
|
||
profile_picture: 'profile.jpg',
|
||
city: 'تهران',
|
||
date_of_birth: '1990-01-01',
|
||
province: 'تهران',
|
||
specialty: 'قلب',
|
||
address: 'خیابان ولیعصر، پلاک ۱۲۳',
|
||
latitude: 35.6892,
|
||
longitude: 51.3890,
|
||
};
|
||
const mockSpecialties = {
|
||
specialties: [
|
||
{ id: 1, name: 'قلب' },
|
||
{ id: 2, name: 'مغز و اعصاب' },
|
||
{ id: 3, name: 'ارتوپدی' },
|
||
],
|
||
};
|
||
|
||
beforeEach(() => {
|
||
jest.clearAllMocks();
|
||
(useUpdateUser as jest.Mock).mockReturnValue({
|
||
mutate: mockUpdateUser,
|
||
isPending: false
|
||
});
|
||
(useGetAllSpecialties as jest.Mock).mockReturnValue({
|
||
data: mockSpecialties
|
||
});
|
||
(toast.error as jest.Mock).mockImplementation(() => {});
|
||
});
|
||
|
||
const renderComponent = (props = {}) => {
|
||
return render(
|
||
<EditInformation
|
||
onclick={mockOnClick}
|
||
userInfo={mockUserInfo}
|
||
{...props}
|
||
/>
|
||
);
|
||
};
|
||
|
||
it('should render correctly with user info', () => {
|
||
renderComponent();
|
||
|
||
expect(screen.getByText('ویرایش اطلاعات')).toBeInTheDocument();
|
||
expect(screen.getByDisplayValue('علی رضایی')).toBeInTheDocument();
|
||
expect(screen.getByDisplayValue('09123456789')).toBeInTheDocument();
|
||
});
|
||
|
||
it('should render with default values when no userInfo provided', () => {
|
||
render(<EditInformation onclick={mockOnClick} />);
|
||
|
||
const nameInput = screen.getByPlaceholderText('نام و نام خانوادگی');
|
||
expect(nameInput).toHaveValue('');
|
||
|
||
const phoneInput = screen.getByPlaceholderText('شماره موبایل');
|
||
expect(phoneInput).toHaveValue('');
|
||
});
|
||
|
||
it('should update name input value', async () => {
|
||
renderComponent();
|
||
|
||
const nameInput = screen.getByDisplayValue('علی رضایی');
|
||
fireEvent.change(nameInput, { target: { value: 'احمد محمدی' } });
|
||
|
||
expect(nameInput).toHaveValue('احمد محمدی');
|
||
});
|
||
|
||
it('should update phone number', async () => {
|
||
renderComponent();
|
||
|
||
const phoneInput = screen.getByDisplayValue('09123456789');
|
||
fireEvent.change(phoneInput, { target: { value: '09987654321' } });
|
||
|
||
expect(phoneInput).toHaveValue('09987654321');
|
||
});
|
||
|
||
it('should call updateUser with correct data on save', async () => {
|
||
mockUpdateUser.mockImplementation((_data: any, { onSuccess }: any) => {
|
||
onSuccess();
|
||
});
|
||
|
||
renderComponent();
|
||
|
||
const saveButton = screen.getByText('ذخیره');
|
||
fireEvent.click(saveButton);
|
||
|
||
await waitFor(() => {
|
||
expect(mockUpdateUser).toHaveBeenCalledWith(
|
||
expect.objectContaining({
|
||
name: 'علی رضایی',
|
||
phone_number: '09123456789',
|
||
specialty: 'قلب',
|
||
province: 'تهران',
|
||
city: 'تهران',
|
||
}),
|
||
expect.any(Object)
|
||
);
|
||
});
|
||
});
|
||
|
||
it('should show error toast on update failure', async () => {
|
||
mockUpdateUser.mockImplementation((_data: any, { onError }: any) => {
|
||
onError(new Error('Update failed'));
|
||
});
|
||
|
||
renderComponent();
|
||
|
||
const saveButton = screen.getByText('ذخیره');
|
||
fireEvent.click(saveButton);
|
||
|
||
await waitFor(() => {
|
||
expect(toast.error).toHaveBeenCalledWith('خطا در به روز رسانی اطلاعات');
|
||
});
|
||
});
|
||
|
||
it('should call onclick after successful save', async () => {
|
||
mockUpdateUser.mockImplementation((_data: any, { onSuccess }: any) => {
|
||
onSuccess();
|
||
});
|
||
|
||
renderComponent();
|
||
|
||
const saveButton = screen.getByText('ذخیره');
|
||
fireEvent.click(saveButton);
|
||
|
||
await waitFor(() => {
|
||
expect(mockOnClick).toHaveBeenCalled();
|
||
});
|
||
});
|
||
|
||
it('should open and close edit photo modal', async () => {
|
||
renderComponent();
|
||
|
||
const profilePhoto = screen.getByAltText('profile');
|
||
fireEvent.click(profilePhoto);
|
||
|
||
expect(screen.getByTestId('edit-photo-modal')).toBeInTheDocument();
|
||
|
||
const closeButton = screen.getByTestId('close-photo-modal');
|
||
fireEvent.click(closeButton);
|
||
|
||
await waitFor(() => {
|
||
expect(screen.queryByTestId('edit-photo-modal')).not.toBeInTheDocument();
|
||
});
|
||
});
|
||
|
||
it('should show hover effect on profile photo', async () => {
|
||
renderComponent();
|
||
|
||
const profileDiv = screen.getByAltText('profile').parentElement!;
|
||
|
||
fireEvent.mouseEnter(profileDiv);
|
||
expect(screen.getByAltText('editIcon')).toBeInTheDocument();
|
||
|
||
fireEvent.mouseLeave(profileDiv);
|
||
await waitFor(() => {
|
||
expect(screen.queryByAltText('editIcon')).not.toBeInTheDocument();
|
||
});
|
||
});
|
||
|
||
it('should disable save button while updating', async () => {
|
||
(useUpdateUser as jest.Mock).mockReturnValue({
|
||
mutate: mockUpdateUser,
|
||
isPending: true
|
||
});
|
||
|
||
renderComponent();
|
||
|
||
const saveButton = screen.getByText('در حال ذخیره...');
|
||
expect(saveButton).toBeDisabled();
|
||
});
|
||
|
||
it('should call onCancel when cancel button is clicked', () => {
|
||
renderComponent();
|
||
|
||
const cancelButton = screen.getByText('انصراف');
|
||
fireEvent.click(cancelButton);
|
||
|
||
expect(mockOnClick).toHaveBeenCalled();
|
||
});
|
||
|
||
it('should enforce maxLength on name input', () => {
|
||
renderComponent();
|
||
|
||
const nameInput = screen.getByDisplayValue('علی رضایی');
|
||
expect(nameInput).toHaveAttribute('maxLength', '30');
|
||
});
|
||
}); |