مش سبع بدائل
المكتبات دي طبقات فوق بعض. كل واحدة ماسكة جزء مختلف:
| المكتبة | دورها | بنستخدم منها إيه | فين |
|---|---|---|---|
vitest | يشغّل tests ويقارن ويعمل mocks | describe، it، expect، hooks، vi.fn، vi.mock، vi.spyOn | ملفات الـ test وsetup.ts وvitest.config.ts |
jsdom | متصفح وهمي جوه Node | document وwindow من غير import | environment: "jsdom" في config |
@testing-library/react | يرسم React والـ hooks | render، renderHook، act، cleanup، screen، within، waitFor | helpers والـ 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 بفحص HTML | toBeInTheDocument، toHaveValue، toHaveAttribute | بيتفعّل مرة في setup.ts |
msw | backend وهمية تمسك requests | setupServer، http.get/post/patch/delete، HttpResponse | server.ts وملفات handlers والـ overrides |
الرحلة جوه test واحدة
Vitestيشغّل test
jsdomيجهّز DOM
renderيرسم React
user-eventيكتب ويدوس
MSWيرد على request
jest-domيفحص الشاشة
Queries تختارها إزاي؟
الأولوية للحاجة اللي اليوزر أو قارئ الشاشة يعرفها:
screen.getByRole("button", { name: "حفظ" });screen.getByLabelText("اسم السنة");screen.findByRole("status");
getBy...: العنصر موجود دلوقتي؛ يرمي error لو مش موجود.queryBy...: بنثبت إن العنصر مش موجود؛ يرجعnull.findBy...: العنصر هيظهر بعد async work؛ بيرجع Promise.
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.