التطبيق · فصل ٦

تفاعل جوه القائمة — حذف Row

user.click · DELETE · route params · waitForElementToBeRemoved
🎯 الهدف: تختار row بعينها، تضغط زرها، وتتأكد من DELETE واختفاء العنصر.

زوّد الحذف للـ component

جوه AcademicYearsList، ضيف function تبعت DELETE وتحدّث القائمة بعد النجاح:

async function removeYear(id: string) {  const response = await fetch(`${API_URL}/${id}`, {    method: "DELETE",  });  if (!response.ok) throw new Error("delete failed");  setYears((current) => current.filter((year) => year.id !== id));}

وزر كل row يبقى:

<button onClick={() => removeYear(year.id)}>  حذف {year.name}</button>

اسم الزر فيه اسم السنة، فمستخدم قارئ الشاشة والـ test يقدروا يفرقوا الأزرار عن بعض.

Test كاملة

import {  render,  screen,  waitForElementToBeRemoved,  within,} from "@testing-library/react";import userEvent from "@testing-library/user-event";import { http, HttpResponse } from "msw";import { expect, it, vi } from "vitest";import { API_URL } from "../test/academicYearsMocks";import { server } from "../test/server";import { AcademicYearsList } from "./AcademicYearsList";it("تحذف السنة المختارة", async () => {  const removed = vi.fn();  server.use(    http.delete(`${API_URL}/:id`, ({ params }) => {      removed(params.id);      return new HttpResponse(null, { status: 204 });    }),  );  const user = userEvent.setup();  render(<AcademicYearsList />);  const yearName = await screen.findByText("2025 / 2026");  const row = yearName.closest("li");  expect(row).not.toBeNull();  await user.click(    within(row!).getByRole("button", {      name: "حذف 2025 / 2026",    }),  );  await waitForElementToBeRemoved(row);  expect(removed).toHaveBeenCalledWith("y1");  expect(screen.getByText("2026 / 2027")).toBeInTheDocument();});

بنثبت إيه؟

  1. البيانات اتحملت فعلًا.
  2. اخترنا زر row المطلوبة، مش أول زر وخلاص.
  3. URL حملت id الصح.
  4. الـ row اختفت بعد نجاح الرد.
  5. باقي القائمة لسه موجود.

PATCH نفس الفكرة

في تعديل السنة، غيّر handler لـ http.patch، افحص params.id وrequest.json()، وبعد success استنى الاسم الجديد يظهر. المبدأ واحد: تفاعل مستخدم، network contract، ثم نتيجة مرئية.

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

🧠 اختبر نفسك
١) ليه اسم الزر «حذف 2025 / 2026» أحسن من «حذف»؟
بيحدد الزر لقارئ الشاشة، وبيخلي اختيار test واضح من غير الاعتماد على ترتيب العناصر.
٢) ليه نفحص السنة التانية بعد الحذف؟
عشان نثبت إننا حذفنا row المطلوبة بس، مش القائمة كلها.
٣) لو الحذف optimistic، أنهي حالة إضافية مهمة؟
رد 500 والـ rollback: العنصر لازم يرجع أو تظهر معالجة الخطأ حسب تصميم المنتج.