البداية · فصل ١

المكتبات السبعة — كل واحدة فين؟

vitest · jsdom · testing-library · user-event · jest-dom · MSW
🎯 الهدف: تعرف دور كل package ومسار الحدث من كتابة اليوزر لحد assertion النهائي.

مش سبع بدائل

المكتبات دي طبقات فوق بعض. كل واحدة ماسكة جزء مختلف:

المكتبةدورهابنستخدم منها إيهفين
vitestيشغّل tests ويقارن ويعمل mocksdescribe، it، expect، hooks، vi.fn، vi.mock، vi.spyOnملفات الـ test وsetup.ts وvitest.config.ts
jsdomمتصفح وهمي جوه Nodedocument وwindow من غير importenvironment: "jsdom" في config
@testing-library/reactيرسم React والـ hooksrender، renderHook، act، cleanup، screen، within، waitForhelpers والـ setup وكل UI tests
@testing-library/domمحرك البحث في الصفحةمش بنستورده غالبًاpeer dependency لـ React Testing Library وuser-event
@testing-library/user-eventيقلّد كتابة وضغط اليوزرsetup، click، type، clearتست الفورم والصفحات
@testing-library/jest-domيزوّد expect بفحص HTMLtoBeInTheDocument، toHaveValue، toHaveAttributeبيتفعّل مرة في setup.ts
mswbackend وهمية تمسك requestssetupServer، http.get/post/patch/delete، HttpResponseserver.ts وملفات handlers والـ overrides

الرحلة جوه test واحدة

Queries تختارها إزاي؟

الأولوية للحاجة اللي اليوزر أو قارئ الشاشة يعرفها:

screen.getByRole("button", { name: "حفظ" });screen.getByLabelText("اسم السنة");screen.findByRole("status");
  • getBy...: العنصر موجود دلوقتي؛ يرمي error لو مش موجود.
  • queryBy...: بنثبت إن العنصر مش موجود؛ يرجع null.
  • findBy...: العنصر هيظهر بعد async work؛ بيرجع Promise.
لو مش عارف تلاقي الزر بـ role وname، ممكن تكون المشكلة accessibility في الـ UI مش في test.

user-event مش fireEvent

user-event يعمل سلسلة أقرب للمتصفح: focus وkeyboard/input/change، ومعظم أوامره async:

const user = userEvent.setup();await user.type(nameInput, "2026 / 2027");await user.click(saveButton);

استخدم fireEvent للحالات المنخفضة المستوى اللي فعلًا محتاج تبعت event واحدة بعينها، مش كاختيار أول.

خلّصت الفصل لما

🧠 اختبر نفسك
١) مين بيرسم React: jsdom ولا Testing Library؟
jsdom يوفّر الـ DOM، وReact Testing Library ترسم الـ component جواه.
٢) رسالة نجاح هتظهر بعد request. getByRole ولا findByRole؟
findByRole لأنها async وبتستنى العنصر يظهر.
٣) مين يمسك POST الحقيقي من component؟
MSW، من خلال handler على نفس method والـ URL.