feat: add testing setup with Vitest and Testing Library

- Updated package.json to include Vitest and Testing Library dependencies and scripts for testing.
- Created a test suite for the ProvinceProvider context to validate cityId and province detection based on subdomains.
- Implemented unit tests for utility functions in helper/index.js, including phone number formatting and validation.
- Added tests for state information retrieval in lib/getStateInfo.js, ensuring correct city and state matching based on subdomains.
- Developed tests for appointment slot adaptation and availability checks in lib/appointmentSlots.js.
- Created tests for token storage functionality in lib/tokenStore.js.
- Implemented sanitization and JSON parsing tests in lib/sanitize.js.
- Added CASL ability tests in lib/ability.js to verify user access rights.
- Created tests for cookie management in lib/refreshCookie.js.
- Developed tests for patient user representation in lib/representationAdapters.js.
- Implemented client-side state information retrieval tests in lib/getStateInfoClient.js.
- Created tests for canonical URL generation in lib/getCanonicalUrl.js.
- Developed tests for clinic API service functions in services/clinicApi.js.
- Added request wrapper tests in services/response.js to ensure correct API interaction.
- Set up Vitest configuration in vitest.config.mjs for JSX support and alias resolution.
- Created setup and utility files for testing environment in test/setup.js and test/utils.jsx.
This commit is contained in:
hamed
2026-06-28 23:25:46 +03:30
parent de9e6b7678
commit c316d22160
12 changed files with 2834 additions and 55 deletions
+76
View File
@@ -0,0 +1,76 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
// --- clinicApi (native fetch) ---
import { getClinicDoctors } from '@/services/clinicApi';
describe('getClinicDoctors', () => {
beforeEach(() => vi.stubGlobal('fetch', vi.fn()));
it('پاسخ double-nested → { data, page }', async () => {
fetch.mockResolvedValue({
ok: true,
json: () =>
Promise.resolve({
data: { data: [{ uuid: 'd1' }, { uuid: 'd2' }], meta: { totalPages: 3, currentPage: 1 } },
}),
});
const out = await getClinicDoctors('my-clinic', { page: 1 });
expect(out.data).toHaveLength(2);
expect(out.page).toEqual({ total_pages: 3, current: 1 });
const calledUrl = fetch.mock.calls[0][0];
expect(calledUrl).toContain('/api/v1/clinic/doctor-list/my-clinic');
});
it('بدون meta → صفحه‌بندی پیش‌فرض', async () => {
fetch.mockResolvedValue({ ok: true, json: () => Promise.resolve({ data: { data: [] } }) });
const out = await getClinicDoctors('x');
expect(out.page).toEqual({ total_pages: 1, current: 1 });
});
it('پاسخ ناموفق (ok=false) → shape خالی پیش‌فرض', async () => {
fetch.mockResolvedValue({ ok: false, status: 500, text: () => Promise.resolve('err') });
const out = await getClinicDoctors('x');
expect(out).toEqual({ data: [], page: { total_pages: 1, current: 1 } });
});
it('خطای شبکه → shape خالی پیش‌فرض', async () => {
fetch.mockRejectedValue(new Error('net'));
const out = await getClinicDoctors('x');
expect(out.data).toEqual([]);
});
});
// --- response.js wrappers (mock axios layer) ---
vi.mock('@/services/api', () => ({
default: {
get: vi.fn(() => Promise.resolve({})),
post: vi.fn(() => Promise.resolve({})),
patch: vi.fn(() => Promise.resolve({})),
delete: vi.fn(() => Promise.resolve({})),
},
}));
import api from '@/services/api';
import { request } from '@/services/response';
describe('request wrappers — قرارداد به لایه‌ی API', () => {
beforeEach(() => {
api.get.mockClear();
api.post.mockClear();
});
it('getUserProfile → GET مسیر درست با requireAuth', () => {
request.getUserProfile('u1');
expect(api.get).toHaveBeenCalledWith('/api/v1/user-profile/u1', { requireAuth: true });
});
it('getUserInfo → GET oauth/userinfo با requireAuth', () => {
request.getUserInfo();
expect(api.get).toHaveBeenCalledWith('oauth/userinfo', { requireAuth: true });
});
it('postAppointment → POST با body و requireAuth', () => {
request.postAppointment({ slot: 5 });
expect(api.post).toHaveBeenCalledWith('api/v1/appointment', { slot: 5 }, { requireAuth: true });
});
});