WisarWisar
Dasturlash kitobi/11-QISM — React37 daqiqa

11.17-bob: Testing — Vitest, React Testing Library, Playwright

11-QISM — Frontend: React · 17-mavzu


1. Kirish va motivatsiya

11-QISM davomida React ilovasini qurishni to'liq o'rgandik: komponent, state, hooklar, routing, forma, performance, animatsiya. Ilovamiz funksional va chiroyli. Lekin u ishonchlimi? Siz yangi feature qo'shganda eski narsa buzilmaganini qaerdan bilasiz? Kodni refactoring qilganda (toza qilganda) hech narsani sindirmaganingizga ishonchingiz bormi? Mahsulot foydalanuvchilar qo'lida kutilgandek ishlashiga kafolat beruvchi narsa — bu test. 11-QISM'ning yakuniy bobi aynan shu — kodga ishonch beruvchi qatlam.

Test — bu "kodni sinab ko'rish"ning avtomatlashtirilgan versiyasi. Qo'lda sinash (har o'zgarishdan keyin ilovani ochib, har tugmani bosib ko'rish) — sekin, zerikarli, va ishonchsiz (biror narsani unutasiz). Avtomatlashtirilgan test — bir marta yoziladi, keyin har o'zgarishda (yoki CI/CD'da — 10.5) avtomatik ishlaydi va "hammasi joyidami?"ni soniyalarda aytadi. Bu — ishonch beradi: refactoring qilasiz (test o'tsa — buzilmagan), yangi feature qo'shasiz (eski testlar o'tsa — regression yo'q), va xato (bug) qayta paydo bo'lmasligini kafolatlaysiz (bug uchun test yozasiz — keyin u qayta kelsa, test darrov tutadi).

Bu bob: nega test (ishonch, refactoring, regression), test turlari (unit, integration, e2e — testing trophy), Vitest (zamonaviy, tez test runner — Jest muqobili), test anatomiyasi (describe/it/expect), React Testing Library falsafasi (foydalanuvchi nuqtai nazaridan — implementatsiya emas), render/screen/queries (getBy/findBy/queryBy), foydalanuvchi interaksiyasi (userEvent), async test (findBy/waitFor), mocking (vi.fn/vi.mock/MSW), custom hook test (renderHook), Playwright (e2e — butun ilovani real brauzerda), va test strategiyasi (nima test qilish, coverage). Bu mavzular to'liq va amaliy misollar bilan ochiladi.

O'xshatish: Test — bu bino qabul komissiyasi va xavfsizlik tekshiruvi. Siz bino qurdingiz (kod yozdingiz) — lekin u xavfsizmi? Liftlar ishlaydimi? Eshiklar ochiladimi? Yong'in chiqsa nima bo'ladi? Qo'lda tekshirish — har xonaga kirib, har tugmani bosib ko'rish (sekin, biror narsani unutasiz). Avtomatik test — bu doimiy nazoratchi robot: har o'zgarishdan keyin butun binoni soniyalarda tekshiradi ("lift ishlaydi , eshik ochiladi , yong'in signali ishlaydi "). Unit test — bitta g'isht (funksiya/komponent) mustahkammi? Integration test — g'ishtlar birga to'g'ri ishlaydimi (forma to'ldirib yuborish)? E2e test — mehmon eshikdan kirib, liftga chiqib, xonaga yetib bora oladimi (butun yo'l — login'dan to'lovgacha)? Test'siz bino — har o'zgarishda "qulamaydimi?" deb qo'rqib yashashingiz kerak; test bilan — ishonch.

Nega muhim?

  • Ishonch — test bilan refactoring/yangi feature qo'rqmasdan qilinadi (eski testlar buzilishni tutadi).
  • Regression — bir marta tuzatilgan bug qayta kelsa, test darrov tutadi (qayta buzilmaydi).
  • Hujjat — test — kodning "qanday ishlashi kerak"ligini ko'rsatadi (jonli misol).
  • Professional standart — jiddiy loyiha test'siz bo'lmaydi; CI/CD 10.5-bob test'ga tayanadi.

2. Nazariya — chuqur tushuntirish

2.1. Nega test va test turlari

text
  TEST — kodni AVTOMATIK sinash (qo'lda har safar tekshirish o'rniga):

  NEGA:
   Ishonch — refactoring/yangi feature (eski test o'tsa — buzilmagan)
   Regression — tuzatilgan bug qayta kelsa — test darrov tutadi
   Hujjat — test kodning qanday ishlashini ko'rsatadi (jonli)
   CI/CD — har push'da avtomatik tekshirish 10.5-bob

  TEST TURLARI (Testing Trophy — Kent C. Dodds):
  ┌─────────────────────────────────────────┐
  │  E2E (kam)         — butun ilova (real brauzer — Playwright) │
  │  INTEGRATION (ko'p)— komponentlar birga (RTL — ASOSIY)      │
  │  UNIT (o'rta)      — funksiya/hook (Vitest)                 │
  │  STATIC            — TypeScript, ESLint (xato yozmasdan)    │
  └─────────────────────────────────────────┘

   Integration test eng ko'p qiymat (komponentlar BIRGA — foydalanuvchi kabi)
   E2e kam, lekin muhim (kritik yo'l — login, to'lov); unit — murakkab mantiq

Nega test va test turlari — test strategiyasining asosi. Nega test: ishonch (refactoring/yangi feature — eski testlar o'tsa buzilmagan), regression (tuzatilgan bug qayta kelsa test tutadi), hujjat (test kodning qanday ishlashini ko'rsatadi — jonli misol), CI/CD (har push'da avtomatik — 10.5). Test turlari (Kent C. Dodds'ning "Testing Trophy" — eski "piramida"ning zamonaviy versiyasi): static (poydevor — TypeScript, ESLint — xato kod yozishni oldini oladi — 11.14, 15.3); unit (alohida funksiya/hook — Vitest); integration (komponentlar birga — React Testing Library — eng ko'p qiymat); e2e (butun ilova real brauzerda — Playwright — kam, lekin muhim). Ikki tamoyil: (1) integration test eng ko'p qiymat beradi — u komponentlarni birga, foydalanuvchi kabi sinaydi (real ishlatish holatiga yaqin); (2) e2e kam (sekin, qimmat — faqat kritik yo'llar: login, to'lov, asosiy oqim), unit esa murakkab mantiq uchun (hisob-kitob, custom hook). To'g'ri balans — ko'p integration, oz e2e, kerakli unit. Bu — samarali test strategiyasi (har joyni 100% emas, muhim joylarni ishonchli).

2.2. Vitest — setup va test anatomiyasi

text
  VITEST — zamonaviy, tez test runner (Vite uchun — Jest muqobili):
  npm install -D vitest @testing-library/react @testing-library/user-event jsdom

  // vite.config.ts — test sozlamasi:
  test: { environment: "jsdom", globals: true, setupFiles: "./src/test/setup.ts" }
  // jsdom — brauzer muhiti (DOM); globals — describe/it/expect global

  TEST FAYLI: Component.test.tsx (yoki .spec.tsx)

  TEST ANATOMIYASI:
  import { describe, it, expect } from "vitest";

  describe("sum funksiyasi", () => {              // guruh (tavsif)
    it("ikki sonni qo'shadi", () => {             // bitta test (holat)
      const result = sum(2, 3);                   // ARRANGE + ACT
      expect(result).toBe(5);                     // ASSERT (tekshiruv)
    });
  });

  AAA NAQSHI: Arrange (tayyorla)  Act (bajar)  Assert (tekshir)

   Vitest — Vite bilan tez, Jest API'si bilan mos (describe/it/expect — bir xil)
   describe (guruh) > it (test) > expect (tekshiruv) — AAA naqshi (tayyor/bajar/tekshir)

Vitest — setup va test anatomiyasi — test yozishning asosi. Vitest — Vite uchun zamonaviy, tez test runner (Jest'ning muqobili, lekin Vite bilan integratsiyalangan va tezroq). O'rnatish: npm install -D vitest @testing-library/react @testing-library/user-event jsdom, va vite.config'da test sozlamasi (environment: "jsdom" — brauzer DOM muhiti, globals: truedescribe/it/expectni global qiladi). Test fayllari *.test.tsx (yoki .spec.tsx). Test anatomiyasi: describe("guruh", () => { it("holat", () => { ...; expect(...).toBe(...) }) })describe (testlar guruhi/tavsif), it (bitta test — bitta holat), expect (tekshiruv — toBe, toEqual, toContain...). AAA naqshi: Arrange (tayyorlash — ma'lumot/komponent), Act (bajarish — funksiyani chaqirish/tugma bosish), Assert (tekshirish — natija kutilgandekmi). Ikki nuqta: (1) Vitest — Vite bilan tez va Jest API'siga mos (describe/it/expect — bir xil sintaksis, Jest'dan o'tish oson); (2) AAA naqshi (Arrange-Act-Assert) — har testni tushunarli, izchil qiladi (avval tayyorla, keyin bajar, oxirida tekshir). Bu — barcha test turlarining umumiy strukturasi.

2.3. React Testing Library falsafasi

text
  RTL FALSAFASI: "foydalanuvchi kabi sina" (implementatsiya emas, XULQ):

   IMPLEMENTATSIYA test (komponent ICHINI tekshirish — mo'rt):
  expect(component.state.count).toBe(5);    // ichki state — refactoring'da buziladi
  //  state nomini o'zgartirsang test buziladi (lekin UI to'g'ri — yolg'on xato)

   XULQ test (foydalanuvchi KO'RADIGANINI tekshirish — ishonchli):
  expect(screen.getByText("Hisob: 5")).toBeInTheDocument();   // foydalanuvchi ko'radigan
  //  ichki qanday qilinsa ham, EKRANda 5 ko'rinsa — test o'tadi (refactoring xavfsiz)

  RTL TAMOYILI: "Testlar ilova ishlatilishiga qancha o'xshasa, shuncha ishonch beradi"
  - foydalanuvchi matnni ko'radi, tugmani bosadi, formani to'ldiradi — TEST ham SHUNDAY
  - komponent ichki tuzilishi (state, props, metod) — TEST BILMASIN (implementatsiya)

   RTL — foydalanuvchi nuqtai nazaridan (ko'radigan, bosadigan); ichki tuzilishni emas
   Bu refactoring'ni XAVFSIZ qiladi (ichni o'zgartirsang ham — UI to'g'ri bo'lsa test o'tadi)

React Testing Library falsafasi — RTL'ning eng muhim tushunchasi (boshqa test kutubxonalardan farqi). Asosiy tamoyil — "foydalanuvchi kabi sina": komponentni uning ichki tuzilishi (state, props, metodlar) orqali emas, foydalanuvchi ko'radigan va qiladigan narsalar orqali sinash. Implementatsiya testi (yomon): expect(component.state.count).toBe(5) — ichki stateni tekshiradi, mo'rt (refactoring'da buziladi — state nomini o'zgartirsangiz test buziladi, garchi UI to'g'ri bo'lsa ham — yolg'on xato). Xulq testi (yaxshi): expect(screen.getByText("Hisob: 5")).toBeInTheDocument() — foydalanuvchi ko'radigan narsani tekshiradi (ichki qanday qilinsa ham, ekranda "5" ko'rinsa test o'tadi). RTL'ning mashhur tamoyili: "Testlar ilova ishlatilishiga qancha o'xshasa, shuncha ishonch beradi" — foydalanuvchi matnni ko'radi, tugmani bosadi, formani to'ldiradi test ham shu tarzda (ichki tuzilishni bilmasin). Ikki nuqta: (1) RTL — foydalanuvchi nuqtai nazaridan (ko'radigan, bosadigan — implementatsiya emas); (2) bu refactoring'ni xavfsiz qiladi (kod ichini o'zgartirsangiz ham, UI bir xil ishlasa test o'tadi — bu testlarning asosiy maqsadi: ishonch berish, to'sqinlik qilmaslik). Bu falsafa — RTL'ni zamonaviy React test standartiga aylantirdi.

2.4. render, screen va queries

text
  render — komponentni test muhitiga chizadi; screen — undan element topadi:
  import { render, screen } from "@testing-library/react";

  render(<Button text="Saqlash" />);            // komponentni chiz
  const btn = screen.getByText("Saqlash");      // undan element top

  QUERY TURLARI (element topish — 3 oila):
  getBy*   — element BOR deb kutadi (yo'q bo'lsa — XATO darrov) — sinxron, eng keng
  queryBy* — element bo'lmasligi mumkin (yo'q bo'lsa — null) — "yo'qligini" tekshirish
  findBy*  — element KEYINROQ paydo bo'ladi (async — Promise) — kutadi 2.6-bob

  QUERY USULLARI (qaysi bo'yicha topish — PRIORITET tartibda):
  getByRole — ARIA roli (button, heading, textbox) — ENG YAXSHI (a11y — 1.9)
  getByLabelText — label (forma maydoni)
  getByText — matn
  getByPlaceholderText, getByTestId (oxirgi chora — data-testid)

   getByRole afzal (foydalanuvchi/ekran o'quvchi kabi — a11y'ni ham tekshiradi)
   getBy (bor) | queryBy (yo'qligini) | findBy (async kutadi — 2.6)

render, screen va queries — komponentni sinashning asosiy vositalari. render(<Component />) — komponentni test muhitiga (jsdom) chizadi; screen — chizilgan komponentdan elementlarni topadi. Query turlari (uch oila — qachon qaysi): getBy* (element bor deb kutadi — yo'q bo'lsa darrov xato — sinxron, eng keng ishlatiladigan), queryBy* (element bo'lmasligi mumkin — yo'q bo'lsa null qaytaradi — "element yo'qligini" tekshirish uchun), findBy* (element keyinroq paydo bo'ladi — async, Promise qaytaradi, kutadi — 2.6). Query usullari (qaysi bo'yicha topish — prioritet tartibda): getByRole (ARIA roli — button, heading, textboxeng yaxshi, chunki foydalanuvchi/ekran o'quvchi kabi va a11y'ni ham tekshiradi — 1.9), getByLabelText (forma maydoni — label bo'yicha), getByText (matn), getByPlaceholderText, va getByTestId (data-testidoxirgi chora, faqat boshqa usul yo'q bo'lsa). Ikki nuqta: (1) getByRole afzal (foydalanuvchi va ekran o'quvchi kabi, va a11y to'g'riligini ham tekshiradi — yaxshi test ham yaxshi a11y'ni rag'batlantiradi); (2) getBy (element bor), queryBy (yo'qligini tekshirish), findBy (async kutadi — 2.6) — har birining o'z vazifasi. Bu — element topishning to'g'ri usuli.

2.5. userEvent — foydalanuvchi interaksiyasi

text
  userEvent — foydalanuvchi harakatini TAQLID qiladi (haqiqiy click/type kabi):
  import userEvent from "@testing-library/user-event";

  it("tugma bosilganda hisob oshadi", async () => {
    const user = userEvent.setup();               // userEvent boshla
    render(<Counter />);

    const btn = screen.getByRole("button", { name: "+" });
    await user.click(btn);                         //  async (haqiqiy click kabi)

    expect(screen.getByText("Hisob: 1")).toBeInTheDocument();
  });

  userEvent USULLARI:
  user.click(el)          — bosish
  user.type(input, "text")— yozish (har harf alohida — haqiqiy kabi)
  user.clear(input)       — tozalash
  user.selectOptions(select, "value")  — tanlash
  user.keyboard("{Enter}")— klaviatura

  fireEvent (eski/past daraja) vs userEvent (yangi — haqiqiyroq):
   userEvent afzal (haqiqiy foydalanuvchi kabi — fokus, hover, har harf)

   userEvent — haqiqiy foydalanuvchi harakatini taqlid (await — async)
   userEvent.setup() bilan boshla; har amal — await (haqiqiy vaqt kabi)

userEvent — foydalanuvchi harakatini (click, type, select) haqiqiy taqlid qiluvchi kutubxona. Ishlatish: const user = userEvent.setup() (boshlash), keyin await user.click(btn), await user.type(input, "text") — har amal await bilan (chunki userEvent haqiqiy foydalanuvchi kabi vaqt oladi — har harf alohida yoziladi, fokus o'zgaradi). Misol: tugmani topib (getByRole("button", { name: "+" })), await user.click(btn), keyin natijani tekshirish (expect(screen.getByText("Hisob: 1"))). userEvent usullari: click (bosish), type (yozish — har harf alohida, haqiqiy kabi), clear (tozalash), selectOptions (tanlash), keyboard("{Enter}") (klaviatura). fireEvent vs userEvent: fireEvent — eski, past daraja (bitta DOM event), userEvent — yangi, haqiqiyroq (fokus, hover, har harf alohida — real foydalanuvchi kabi). Ikki nuqta: (1) userEvent afzal (fireEvent'dan) — u haqiqiy foydalanuvchi xatti-harakatini taqlid qiladi (har harf type, fokus, hover — real test); (2) userEvent.setup() bilan boshla va har amalni await qil (haqiqiy vaqt kabi — async). Bu — interaktiv komponentlarni (forma, tugma, hisoblagich) foydalanuvchi nuqtai nazaridan sinashning standarti.

2.6. Async test — findBy va waitFor

text
  ASYNC — ma'lumot KEYINROQ paydo bo'ladi (fetch, setTimeout)  kutish kerak:

  // findBy* — element KELANGUNCHA kutadi (async query — Promise):
  it("ma'lumot yuklangach ko'rsatadi", async () => {
    render(<UserList />);                          // fetch boshlanadi (yuklanmoqda)

    //  findBy — element paydo bo'lguncha kutadi (max 1s default)
    const user = await screen.findByText("Ali");  // ma'lumot kelgach "Ali" paydo bo'ladi
    expect(user).toBeInTheDocument();
  });

  // waitFor — biror shart bajarilguncha kutadi (murakkabroq holat):
  await waitFor(() => {
    expect(screen.getByText("Yuklandi")).toBeInTheDocument();
  });

  // Yo'qolishni kutish (loading tugashi):
  await waitForElementToBeRemoved(() => screen.queryByText("Yuklanmoqda..."));

  ┌────────────────────────────────────────────────────────────┐
  │ findBy: element paydo bo'lishini kutadi | waitFor: shart      │
  │ getBy: darrov (sinxron) | findBy: async (kutadi)            │
  └────────────────────────────────────────────────────────────┘

   Async ma'lumot (fetch)  findBy (kutadi); getBy DARROV (yo'q bo'lsa xato)
   waitFor — murakkab shart kutish; findByText — element paydo bo'lishi (eng keng)

Async test — findBy va waitFor — keyinroq paydo bo'ladigan ma'lumotni sinash. Muammo: komponent serverdan ma'lumot olsa (fetch — 11.5), u darrov ekranda yo'q (avval "yuklanmoqda", keyin ma'lumot) — getBy* (sinxron — darrov tekshiradi) xato beradi (element hali yo'q). Yechim — async query'lar: findBy* — element paydo bo'lguncha kutadi (Promise qaytaradi, default 1 soniya): const user = await screen.findByText("Ali") (ma'lumot kelgach "Ali" paydo bo'ladi, findBy uni kutadi). waitFor — murakkabroq shart bajarilguncha kutadi: await waitFor(() => expect(screen.getByText("Yuklandi")).toBeInTheDocument()). waitForElementToBeRemoved — element yo'qolishini kutadi (loading spinner tugashi — await waitForElementToBeRemoved(() => screen.queryByText("Yuklanmoqda..."))). Ikki nuqta: (1) async ma'lumot (fetch, taymer) findBy (element paydo bo'lishini kutadi); getBy faqat darrov mavjud element uchun (yo'q bo'lsa xato); (2) waitFor — murakkab shart (bir nechta yoki maxsus holat), findByText — element paydo bo'lishi (eng keng ishlatiladigan). Bu — real (serverga ulangan) komponentlarni sinashning zarur qismi (deyarli har komponent ma'lumot oladi).

2.7. Mocking — vi.fn, vi.mock va MSW

text
  MOCK — haqiqiy bog'liqlikni SOXTA bilan almashtirish (test izolyatsiya + nazorat):

  // 1. vi.fn() — soxta funksiya (chaqirilganini tekshirish):
  const onClick = vi.fn();                        // soxta funksiya
  render(<Button onClick={onClick} />);
  await user.click(screen.getByRole("button"));
  expect(onClick).toHaveBeenCalledTimes(1);       // chaqirildimi? (callback test)

  // 2. vi.mock() — butun modulni mock (masalan API moduli):
  vi.mock("./api", () => ({ getUser: vi.fn(() => Promise.resolve({ name: "Ali" })) }));

  // 3. MSW (Mock Service Worker) — TARMOQ darajasida mock (eng yaxshi — haqiqiyroq):
  // server.use(http.get("/api/users", () => HttpResponse.json([{ name: "Ali" }])));
  //  komponent haqiqiy fetch qiladi, lekin MSW soxta javob beradi (real kabi)

  ┌────────────────────────────────────────────────────────────┐
  │ vi.fn(): callback/funksiya | vi.mock(): modul | MSW: tarmoq │
  │ MSW eng yaxshi (haqiqiy fetch + soxta javob — real kabi)    │
  └────────────────────────────────────────────────────────────┘

   Mock — bog'liqlikni (API, funksiya) soxta bilan (test izolyatsiya, tez, nazorat)
   MSW — API mock'ning eng yaxshi usuli (tarmoq darajasida — komponent haqiqiy so'rov)

Mocking — haqiqiy bog'liqlikni (API, funksiya, modul) soxta (mock) bilan almashtirish (test izolyatsiya, tezlik, nazorat uchun). Uch usul: (1) vi.fn() — soxta funksiya (callback test): const onClick = vi.fn() <Button onClick={onClick} /> click expect(onClick).toHaveBeenCalledTimes(1) (funksiya chaqirildimi, qanday argument bilan — tekshiriladi). (2) vi.mock() — butun modulni mock (masalan API moduli — haqiqiy serverga bormasin): vi.mock("./api", () => ({ getUser: vi.fn(() => Promise.resolve({ name: "Ali" })) })). (3) MSW (Mock Service Worker)tarmoq darajasida mock (eng yaxshi usul): komponent haqiqiy fetch qiladi, lekin MSW so'rovni "ushlab", soxta javob beradi (real kabi — komponent kodini o'zgartirish kerak emas). Ikki nuqta: (1) mock — bog'liqlikni soxta bilan almashtiradi (test izolyatsiyalangan, tez — haqiqiy serverga bog'liq emas, va nazoratli — soxta javobni siz belgilaysiz); (2) MSW — API mock'ning eng yaxshi usuli (tarmoq darajasida — komponent haqiqiy so'rov yuboradi, MSW javob beradi — bu eng "haqiqiy" va ishonchli). vi.fn callback'lar uchun, MSW API'lar uchun ideal. Mocking — komponentni tashqi bog'liqliksiz, tez va ishonchli sinashning kaliti.

2.8. Custom hook test — renderHook

text
  CUSTOM HOOK 11.7-bob — komponentsiz sinash (renderHook):
  import { renderHook, act } from "@testing-library/react";

  it("useCounter oshadi", () => {
    const { result } = renderHook(() => useCounter(0));   // hook'ni "render" qiladi

    expect(result.current.count).toBe(0);                 // boshlang'ich qiymat

    act(() => {                                           //  state o'zgarishni act bilan o'ra
      result.current.increment();                         // hook funksiyasini chaqir
    });

    expect(result.current.count).toBe(1);                 // o'zgardimi?
  });

  - renderHook(() => useX()) — hook'ni komponentsiz ishga tushiradi
  - result.current — hook qaytargan joriy qiymat
  - act() — state o'zgarishni o'raydi (React ogohlantirishini oldini oladi)

   renderHook — custom hook'ni komponentsiz sinash (mantiq izolyatsiya — 11.7)
   act() — state o'zgartiruvchi amalni o'ra (re-render to'g'ri bo'lsin)

Custom hook test — renderHook — custom hook'larni 11.7-bob komponentsiz sinash. renderHook — hook'ni alohida (komponentga solmasdan) ishga tushiradi: const { result } = renderHook(() => useCounter(0)). result.current — hook qaytargan joriy qiymat (result.current.count, result.current.increment). State o'zgartiruvchi amalni act() bilan o'rash kerak: act(() => { result.current.increment() }) — bu React'ga state o'zgarishi va re-render to'g'ri bo'lishini ta'minlaydi (act'siz React ogohlantiradi). Naqsh: boshlang'ich qiymatni tekshir act ichida hook funksiyasini chaqir yangi qiymatni tekshir. Ikki nuqta: (1) renderHook — custom hook'ni komponentsiz sinash (hook'ning mantig'i UI'dan ajralgan — 11.7: 2.14 — shuning uchun alohida, oson sinaladi); (2) act() — state o'zgartiruvchi amallarni o'rash (increment, setState — re-render to'g'ri kuzatilsin). Bu — custom hook'larni ishonchli sinashning standarti (hook — mantiq, mantiqni alohida sinash — toza, fokuslangan test). Murakkab hook'lar (useFetch, useDebounce) uchun ayniqsa foydali.

2.9. Playwright — e2e setup va test

text
  PLAYWRIGHT — E2E (end-to-end): butun ilovani REAL brauzerda sinash:
  npm init playwright@latest

  E2E — foydalanuvchi KABI butun OQIMni sinash (real brauzer, real navigatsiya):
  import { test, expect } from "@playwright/test";

  test("foydalanuvchi login qiladi", async ({ page }) => {
    await page.goto("/login");                              // sahifaga bor
    await page.getByLabel("Email").fill("ali@x.uz");        // formani to'ldir
    await page.getByLabel("Parol").fill("12345678");
    await page.getByRole("button", { name: "Kirish" }).click();   // tugma bos

    await expect(page).toHaveURL("/dashboard");             //  dashboard'ga o'tdimi?
    await expect(page.getByText("Xush kelibsiz")).toBeVisible();
  });

  RTL vs PLAYWRIGHT:
  - RTL (integration) — jsdom (soxta brauzer), bitta komponent/sahifa (tez)
  - Playwright (e2e) — REAL brauzer (Chrome/Firefox/Safari), butun ilova (sekinroq)

   Playwright — butun oqim (logindashboardamal) real brauzerda (kritik yo'l)
   E2e KAM (sekin, qimmat) — faqat kritik oqim (login, to'lov, asosiy yo'l — 2.1)

Playwright — e2e — butun ilovani real brauzerda sinash (end-to-end). O'rnatish: npm init playwright@latest. E2e — foydalanuvchi kabi butun oqimni sinaydi (real brauzer — Chrome/Firefox/Safari, real navigatsiya, real klick): test("login", async ({ page }) => { await page.goto("/login"); await page.getByLabel("Email").fill(...); await page.getByRole("button", { name: "Kirish" }).click(); await expect(page).toHaveURL("/dashboard") }) — foydalanuvchi login sahifasiga boradi, formani to'ldiradi, tugmani bosadi, va haqiqatan dashboard'ga o'tganini tekshiradi. RTL vs Playwright: RTL (integration) — jsdom (soxta brauzer), bitta komponent/sahifa, tez; Playwright (e2e) — real brauzer, butun ilova (login'dan to'lovgacha — to'liq oqim), sekinroq lekin eng ishonchli. Ikki nuqta: (1) Playwright — butun oqimni (login dashboard amal — bir necha sahifa orqali) real brauzerda sinaydi (haqiqiy foydalanuvchi tajribasiga eng yaqin); (2) e2e kam ishlatiladi (sekin, qimmat, mo'rt) — faqat kritik yo'llar (login, to'lov, asosiy xarid oqimi — 2.1 testing trophy). To'g'ri balans — ko'p RTL integration test + bir nechta kritik Playwright e2e test. Playwright — eng yuqori ishonch darajasini beradi (real brauzer, real oqim).

2.10. Test strategiyasi — nima test qilish

text
  NIMA TEST QILISH (har joyni emas — MUHIM joylarni):

   TEST QIL:
  - Murakkab mantiq (hisob-kitob, validatsiya, custom hook — 2.8)
  - Foydalanuvchi oqimlari (forma yuborish, login, qidiruv)
  - Kritik yo'llar (to'lov, ro'yxatdan o'tish — e2e — 2.9)
  - Tuzatilgan bug (regression — qayta kelmasin)
  - Shartli mantiq (turli holatlar — empty/error/data — 11.15: 2.8)

   TEST QILMA (kam qiymat):
  - Oddiy ko'rsatish (faqat props chizadigan komponent)
  - Tashqi kutubxona (React/Swiper — ular o'zi sinalgan)
  - Implementatsiya tafsiloti (ichki state — 2.3)

  COVERAGE (qamrov) — qancha kod test qilingan (%):
  - 100% maqsad EMAS (foydasiz testlar — vaqt isrofi)
  - MUHIM kod (mantiq, oqim) yaxshi qamralsin (sifat > raqam)

   Test — MUHIM joylarga (mantiq, oqim, bug); har qatorga emas (sifat > coverage raqami)
   Bug topilsa — avval TEST yoz (qayta kelmasin), keyin tuzat (TDD ruhi)

Test strategiyasi — nima test qilish — testlarni qaerga yo'naltirish (har joyni 100% emas, muhim joylarni). Test qilish kerak: murakkab mantiq (hisob-kitob, validatsiya, custom hook — 2.8), foydalanuvchi oqimlari (forma yuborish, login, qidiruv), kritik yo'llar (to'lov, ro'yxatdan o'tish — e2e — 2.9), tuzatilgan bug (regression — qayta kelmasligini kafolatlash), shartli mantiq (empty/error/data holatlari — 11.15: 2.8). Test qilmaslik (kam qiymat): oddiy ko'rsatish (faqat props chizadigan komponent — sinashga arzimadi), tashqi kutubxona (React, Swiper — ular o'zi sinalgan), implementatsiya tafsiloti (ichki state — 2.3). Coverage (qamrov — qancha kod test qilingan %): 100% maqsad emas (foydasiz testlar vaqt isrofi va mo'rt) — muhim kod (mantiq, oqim) yaxshi qamralsin (sifat > raqam). Ikki tamoyil: (1) test — muhim joylarga (mantiq, foydalanuvchi oqimi, bug); har qatorga emas (coverage raqamini quvmaslik — sifat muhim); (2) bug topilsa — avval uni qaytaradigan test yoz (regression — qayta kelmasin), keyin tuzat (TDD — Test-Driven Development ruhi). Bu — samarali, qiymatli test strategiyasi (kam, lekin to'g'ri test > ko'p, lekin foydasiz test).

2.11. Test hayotiy sikli — hook, spyOn, fake timer va coverage

text
  TEST HOOK'LARI (setup/teardown — har test toza boshlansin):
  beforeEach(() => { ... })   — HAR test'dan OLDIN (tayyorlash — masalan mock reset)
  afterEach(() => { ... })    — HAR test'dan KEYIN (tozalash)
  beforeAll / afterAll        — bir marta (guruh boshi/oxiri — MSW server — Misol 8)

  MOCK VOSITALARI:
  vi.fn()      — soxta funksiya (callback — 2.7)
  vi.spyOn(obj, "metod")  — MAVJUD metodni kuzatish (asl saqlanadi yoki mock qilinadi)
  vi.mock("./modul")      — butun modul 2.7-bob
  vi.clearAllMocks()      — chaqiruv tarixini tozalash (beforeEach ichida — izolyatsiya)

  // spyOn misoli — console.error chaqirilganini tekshirish (aslini bosmasdan):
  const spy = vi.spyOn(console, "error").mockImplementation(() => {});
  // ... test ...
  expect(spy).toHaveBeenCalled();

  FAKE TIMER — vaqtni "tezlashtirish" (setTimeout/debounce testi — real kutmasdan):
  vi.useFakeTimers();
  render(<Debounced />);
  vi.advanceTimersByTime(500);   // 500ms "o'tdi" (real 500ms kutmaymiz)
  vi.useRealTimers();            // oxirida qaytar

  COVERAGE (qamrov — c8 yoki istanbul provayder):
  // vite.config: test.coverage = { provider: "v8", reporter: ["text","html"] }
  npm run test -- --coverage     // qaysi qatorlar test qilingan — hisobot

   beforeEach + clearAllMocks — har test IZOLYATSIYA (biri boshqasiga ta'sir qilmasin)
   Fake timer — taymer/debounce testini TEZ va DETERMINISTIK qiladi (real vaqt kutmasdan)

Test hayotiy sikli — hook, spyOn, fake timer va coverage — testlarni toza, izolyatsiyalangan va determinlashgan qiluvchi vositalar. Setup/teardown hook'lari: beforeEach (har test'dan oldin — masalan vi.clearAllMocks() bilan mock tarixini tozalash), afterEach (har test'dan keyin — tozalash; RTL DOM'ni avtomatik tozalaydi), beforeAll/afterAll (guruh boshi/oxirida bir marta — MSW serverni ishga tushirish/to'xtatish — Misol 8). Mock vositalari: vi.fn() (soxta funksiya — 2.7), vi.spyOn(obj, "metod") (mavjud metodni kuzatish — chaqirildimi, qanday argument bilan; asl xulqni saqlash yoki mockImplementation bilan almashtirish mumkin — masalan console.errorni bosmasdan tekshirish), vi.mock() (butun modul), vi.clearAllMocks() (chaqiruv tarixini tozalash — beforeEachda izolyatsiya uchun). Fake timer — vaqtga bog'liq kodni (debounce, setTimeout, polling — 11.7) real kutmasdan sinash: vi.useFakeTimers() vi.advanceTimersByTime(500) (500ms "o'tkazadi") vi.useRealTimers() (oxirida qaytarish). Coverage (qamrov) — Vitest v8 (c8) yoki istanbul provayder bilan qaysi qatorlar test qilinganini hisoblaydi (--coverage bayrog'i, reporter: ["text", "html"]). Ikki nuqta: (1) beforeEach + vi.clearAllMocks() har testni izolyatsiya qiladi (biri ikkinchisiga ta'sir qilmasin — flaky testlarning oldini oladi — Xato 5); (2) fake timer taymer/debounce testini tez va deterministik qiladi (real vaqt kutish — sekin va beqaror). Bu vositalar — ishonchli, takrorlanadigan test poydevorini quradi.

2.12. Snapshot test va router test

text
  SNAPSHOT TEST — komponent chiqishini "suratga olib" saqlaydi, keyin taqqoslaydi:
  expect(container).toMatchSnapshot();   // 1-ishga tushirish: __snapshots__ ga yozadi
  // keyingi ishlar: chiqish o'zgarsa — test yiqiladi (diff ko'rsatiladi)

  QACHON FOYDALI:  barqaror, murakkab chiqish (masalan formatlangan matn, config)
  QACHON ZARARLI:  tez-tez o'zgaradigan UI (har o'zgarishda snapshot yangilanadi
                    "u --update" refleks bilan bosiladi  test qiymatini yo'qotadi)
   Snapshot — EHTIYOTKORLIK bilan (katta UI snapshot'i mo'rt — xulq testi afzal — 2.3)

  ROUTER TEST — routing'ga bog'liq komponent 11.9-bob MemoryRouter bilan:
  import { MemoryRouter } from "react-router-dom";

  it("URL'ga qarab to'g'ri sahifa ko'rinadi", () => {
    render(
      <MemoryRouter initialEntries={["/users/5"]}>   // boshlang'ich URL
        <Routes><Route path="/users/:id" element={<UserPage />} /></Routes>
      </MemoryRouter>
    );
    expect(screen.getByText(/foydalanuvchi 5/i)).toBeInTheDocument();
  });

   MemoryRouter — brauzersiz (xotirada) routing test (initialEntries — boshlang'ich yo'l)

Snapshot test va router test — ikkita qo'shimcha, lekin muhim texnika. Snapshot test — komponentning render chiqishini birinchi ishga tushishda "surat" qilib (__snapshots__ papkasiga) saqlaydi, keyingi ishlarda joriy chiqishni saqlangan bilan taqqoslaydi: expect(container).toMatchSnapshot(). Agar chiqish o'zgarsa — test yiqiladi va farq (diff) ko'rsatiladi. Qachon foydali: barqaror va murakkab chiqish (formatlangan matn, konfiguratsiya obyekti, kichik saf komponent). Qachon zararli: tez-tez o'zgaradigan UI — har o'zgarishda snapshot "yangilanadi" (--update), dasturchi diff'ni o'qimasdan reflektorik tasdiqlaydi test o'z qiymatini yo'qotadi (yolg'on xotirjamlik). Shuning uchun snapshot ehtiyotkorlik bilan — katta UI snapshot'i mo'rt; ko'p holatda aniq xulq testi (getByText, getByRole — 2.3) afzal. Router test — routing'ga bog'liq komponentni (useParams, Link, useNavigate — 11.9) MemoryRouter bilan sinash: u brauzersiz, xotirada routing beradi, initialEntries={["/users/5"]} bilan boshlang'ich URL belgilanadi. Misolda /users/:id yo'liga mos komponent to'g'ri sahifani ko'rsatishi tekshiriladi. MemoryRouter — brauzer manzil satriga bog'liq bo'lmagan, tez va izolyatsiyalangan routing test'ini beradi (real brauzer navigatsiyasi — Playwright e2e ishi — 2.9).

2.13. Yordamchi query'lar — within, rerender, cleanup; a11y test va TDD

text
  QO'SHIMCHA RTL VOSITALARI:
  within(el)  — element ICHIDA qidirish (masalan bitta ro'yxat elementi ichida)
  const row = screen.getByRole("row", { name: /Ali/ });
  within(row).getByRole("button", { name: "O'chirish" });   // shu QATOR ichida

  const { rerender } = render(<Badge count={1} />);
  rerender(<Badge count={2} />);   // BOSHQA props bilan qayta render (props o'zgarishi testi)

  cleanup()   — DOM'ni tozalash (RTL afterEach'da AVTOMATIK — globals:true — 2.2)

  A11Y TEST (jest-axe) — avtomatik accessibility tekshiruvi:
  import { axe } from "jest-axe";
  const { container } = render(<Form />);
  expect(await axe(container)).toHaveNoViolations();   // a11y buzilishi bormi?

  TDD (Test-Driven Development) — avval TEST, keyin KOD:
  1. Red    — test yoz (hali kod yo'q — yiqiladi)
  2. Green  — minimal kod yoz (test o'tsin)
  3. Refactor — kodni tozala (test yashil qoladi — ishonch)

   within — chegaralangan qidiruv (bir nechta o'xshash element orasidan aniq topish)
   jest-axe — a11y'ni AVTOMATIK tekshiradi (getByRole a11y'ni rag'batlantiradi — 2.4)

Yordamchi query'lar, a11y test va TDD — testni aniq va sifatli qiluvchi vositalar. within(element) — berilgan element ichida qidiradi (butun ekranda emas): agar bir nechta o'xshash element bo'lsa (masalan har biri "O'chirish" tugmasiga ega ro'yxat qatorlari), avval kerakli qatorni topib (getByRole("row", { name: /Ali/ })), keyin uning ichida aniq tugmani topish mumkin (within(row).getByRole("button", ...)) — bu "bir nechta element topildi" xatosining (Xato 6) toza yechimi. rerenderrender qaytaradigan funksiya; komponentni boshqa props bilan qayta render qiladi (rerender(<Badge count={2} />)) — props o'zgarishiga komponent qanday javob berishini sinash uchun. cleanup — test orasida DOM'ni tozalaydi (RTL globals: true bilan afterEachda avtomatik chaqiriladi — 2.2 — shuning uchun odatda qo'lda yozilmaydi). A11y test (jest-axe) — komponentning accessibility (a11y — 1.9) buzilishlarini avtomatik tekshiradi: expect(await axe(container)).toHaveNoViolations() — rang kontrasti, yetishmayotgan label, noto'g'ri ARIA kabi muammolarni tutadi. TDD (Test-Driven Development) — avval test, keyin kod yozish sikli: Red (test yoz — kod yo'q, yiqiladi) Green (minimal kod yoz — test o'tsin) Refactor (kodni tozala — test yashil qoladi). Ikki nuqta: (1) within — chegaralangan, aniq qidiruv (o'xshash elementlar orasidan to'g'risini tanlash); (2) jest-axe a11y'ni avtomatik tekshiradi va getByRole 2.4-bob bilan birga yaxshi, foydalanuvchiga qulay UI'ni rag'batlantiradi (yaxshi test = yaxshi a11y).

2.14. Playwright — locator, auto-wait, fixture va CI

text
  PLAYWRIGHT ILG'OR — locator, avtomatik kutish, fixture:
  LOCATOR — element "ko'rsatkichi" (kech baholanadi — auto-wait bilan):
  page.getByRole("button", { name: "Kirish" })   — RTL kabi (role/label/text afzal)
  page.getByLabel("Email") / page.getByText(...) / page.locator("css")

  AUTO-WAIT — Playwright element TAYYOR bo'lishini AVTOMATIK kutadi (sleep KERAK EMAS):
  await page.getByRole("button").click();   // ko'rinadigan/bosiladigan bo'lguncha kutadi
  await expect(page.getByText("OK")).toBeVisible();   // web-first assertion (kutadi)

  AUTH SETUP — bir marta login qilib, holatni saqlab qayta ishlatish (tezlik):
  // setup: login  context.storageState({ path: "auth.json" })
  // test:  use: { storageState: "auth.json" }   — har test login qilmaydi

  FIXTURE — test uchun tayyor kontekst (page, browser — o'rnatilgan; maxsus ham yoziladi)
  DIAGNOSTIKA:  trace (qadam-baqadam yozuv), screenshot, video (xato bo'lganda)
  PARALLEL — testlar parallel ishlaydi (tez); CI'da headless (10.5 — GitHub Actions)

   Playwright locator'lari RTL kabi (getByRole/Label afzal — bir xil falsafa)
   Auto-wait — flaky'ni kamaytiradi (sleep emas — element tayyor bo'lguncha kutadi — Xato 7)

Playwright — locator, auto-wait, fixture va CI — e2e testni ishonchli va tez qiluvchi ilg'or imkoniyatlar. Locator — element "ko'rsatkichi" (darrov emas, ishlatilganda kech baholanadi): Playwright RTL bilan bir xil falsafada getByRole, getByLabel, getByTextni afzal ko'radi (CSS/XPath — oxirgi chora), shuning uchun RTL'dan Playwright'ga o'tish tabiiy. Auto-wait — Playwright har amaldan (click, fill) oldin element tayyor (ko'rinadigan, bosiladigan) bo'lishini avtomatik kutadi, va await expect(locator).toBeVisible() kabi "web-first" assertion'lar shart bajarilguncha kutadi — shuning uchun qo'lda sleep yozish kerak emas (bu flaky testlarning asosiy sababini — 2.9, Xato 7 — yo'qotadi). Auth setup — kritik optimizatsiya: har e2e test'da qaytadan login qilish sekin; buning o'rniga bir marta login qilib brauzer holatini (storageState) faylga saqlab, keyingi testlarda qayta ishlatish mumkin (use: { storageState: "auth.json" }). Fixture — testga tayyor kontekst beradigan mexanizm (page, browser — o'rnatilgan; loyihaga xos fixture'lar ham yoziladi — masalan "loginlangan sahifa"). Diagnostika — xato bo'lganda Playwright trace (qadam-baqadam takroriy yozuv), screenshot va video saqlaydi (CI'da nima buzilganini ko'rish oson). Parallel va CI — testlar parallel (tez) ishlaydi, CI'da headless (ekransiz) rejimda (10.5 — GitHub Actions). Ikki nuqta: (1) Playwright locator'lari RTL kabi (getByRole/getByLabel afzal — bir xil "foydalanuvchi kabi" falsafa — 2.3); (2) auto-wait flaky testni keskin kamaytiradi (sleep emas — element tayyor bo'lguncha kutadi). Bu imkoniyatlar Playwright'ni CI'da barqaror ishlaydigan professional e2e vositasiga aylantiradi.


3. Sintaksis — tez ma'lumotnoma

text
TEST 2.2-bob:        describe("guruh", () => { it("holat", () => { expect(x).toBe(y) }) })
RENDER 2.4-bob:      render(<Component/>)  +  screen.getByRole("button", {name:"..."})
QUERY 2.4-bob:       getBy (bor) | queryBy (yo'q) | findBy (async) | *ByRole/Text/LabelText
USER 2.5-bob:        const user=userEvent.setup(); await user.click(el) / user.type(el,"x")
ASYNC 2.6-bob:       await screen.findByText("...")  |  await waitFor(() => expect(...))
MOCK 2.7-bob:        vi.fn()  |  vi.mock("./api")  |  MSW (http.get + HttpResponse.json)
HOOK 2.8-bob:        const {result}=renderHook(()=>useX()); act(()=>result.current.fn())
LIFECYCLE 2.11-bob:  beforeEach/afterEach/beforeAll | vi.spyOn(obj,"m") | vi.clearAllMocks()
TIMER 2.11-bob:      vi.useFakeTimers(); vi.advanceTimersByTime(500); vi.useRealTimers()
ROUTER 2.12-bob:     <MemoryRouter initialEntries={["/x/5"]}><Routes>...</Routes></MemoryRouter>
WITHIN 2.13-bob:     within(row).getByRole("button") | rerender(<C prop={2}/>) | toMatchSnapshot()
A11Y 2.13-bob:       expect(await axe(container)).toHaveNoViolations()
EXPECT:            toBe | toEqual | toBeInTheDocument | toHaveBeenCalledWith | toHaveTextContent
PLAYWRIGHT 2.9-bob:  test("...", async({page})=>{ await page.goto("/"); await expect(page)... })
PW-AUTH 2.14-bob:    context.storageState({path:"auth.json"})  +  use:{storageState:"auth.json"}

4. Batafsil kod namunalari

Misol 1 — Vitest: oddiy funksiya test (2.2)

ts
import { describe, it, expect } from "vitest";
import { calculateTotal } from "./cart";

describe("calculateTotal", () => {
  it("mahsulotlar summasini hisoblaydi", () => {
    const items = [{ price: 100, qty: 2 }, { price: 50, qty: 1 }];
    expect(calculateTotal(items)).toBe(250);          // 100*2 + 50*1
  });

  it("bo'sh savatda 0 qaytaradi", () => {
    expect(calculateTotal([])).toBe(0);
  });
});

Misol 2 — Komponent render test (2.4)

tsx
import { render, screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import { UserCard } from "./UserCard";

describe("UserCard", () => {
  it("foydalanuvchi ma'lumotini ko'rsatadi", () => {
    render(<UserCard name="Ali" role="admin" />);

    expect(screen.getByText("Ali")).toBeInTheDocument();           // matn ko'rinadi
    expect(screen.getByText("admin")).toBeInTheDocument();
    expect(screen.getByRole("heading", { name: "Ali" })).toBeInTheDocument();  // role bilan
  });
});

Misol 3 — userEvent: hisoblagich (2.5)

tsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

it("tugma bosilganda hisob oshadi", async () => {
  const user = userEvent.setup();
  render(<Counter />);

  expect(screen.getByText("Hisob: 0")).toBeInTheDocument();        // boshlang'ich

  await user.click(screen.getByRole("button", { name: "+" }));     // bos
  expect(screen.getByText("Hisob: 1")).toBeInTheDocument();        // oshdi

  await user.click(screen.getByRole("button", { name: "+" }));
  expect(screen.getByText("Hisob: 2")).toBeInTheDocument();
});

Misol 4 — Forma test (userEvent — 2.5)

tsx
it("forma to'ldirilganda yuboriladi", async () => {
  const user = userEvent.setup();
  const onSubmit = vi.fn();                                        // soxta callback (2.7)
  render(<LoginForm onSubmit={onSubmit} />);

  await user.type(screen.getByLabelText("Email"), "ali@x.uz");     // yoz
  await user.type(screen.getByLabelText("Parol"), "12345678");
  await user.click(screen.getByRole("button", { name: "Kirish" }));

  expect(onSubmit).toHaveBeenCalledWith({ email: "ali@x.uz", password: "12345678" });  // to'g'ri ma'lumot
});

Misol 5 — Validatsiya xatosini test (2.4, 2.6)

tsx
it("noto'g'ri email xatosi ko'rsatiladi", async () => {
  const user = userEvent.setup();
  render(<LoginForm />);

  await user.type(screen.getByLabelText("Email"), "notemail");     // noto'g'ri
  await user.click(screen.getByRole("button", { name: "Kirish" }));

  // findBy — xato async paydo bo'ladi (validatsiya — 11.10)
  expect(await screen.findByText("Email noto'g'ri")).toBeInTheDocument();
});

Misol 6 — Async ma'lumot test (findBy — 2.6)

tsx
it("ma'lumot yuklangach ro'yxatni ko'rsatadi", async () => {
  render(<UserList />);                                            // fetch boshlanadi

  // 1. avval loading
  expect(screen.getByText("Yuklanmoqda...")).toBeInTheDocument();

  // 2. findBy — ma'lumot kelguncha kutadi
  expect(await screen.findByText("Ali")).toBeInTheDocument();
  expect(screen.getByText("Vali")).toBeInTheDocument();

  // 3. loading yo'qoldi
  expect(screen.queryByText("Yuklanmoqda...")).not.toBeInTheDocument();  // queryBy (yo'qligini)
});

Misol 7 — Mock funksiya (vi.fn — 2.7)

tsx
it("o'chirish tugmasi onDelete'ni chaqiradi", async () => {
  const user = userEvent.setup();
  const onDelete = vi.fn();                                        // soxta funksiya
  render(<TodoItem todo={{ id: "1", text: "Sport" }} onDelete={onDelete} />);

  await user.click(screen.getByRole("button", { name: /o'chir/i }));

  expect(onDelete).toHaveBeenCalledTimes(1);                       // bir marta chaqirildi
  expect(onDelete).toHaveBeenCalledWith("1");                      // to'g'ri id bilan
});

Misol 8 — API mock (MSW — 2.7)

tsx
import { http, HttpResponse } from "msw";
import { setupServer } from "msw/node";

const server = setupServer(
  http.get("/api/users", () => HttpResponse.json([{ id: "1", name: "Ali" }]))   // soxta javob
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

it("API'dan kelgan foydalanuvchilarni ko'rsatadi", async () => {
  render(<UsersPage />);                                           // haqiqiy fetch (MSW ushlaydi)
  expect(await screen.findByText("Ali")).toBeInTheDocument();      // soxta javobdan
});
//  Komponent HAQIQIY fetch qiladi, MSW soxta javob beradi (real kabi — 2.7)

Misol 9 — Custom hook test (renderHook — 2.8)

tsx
import { renderHook, act } from "@testing-library/react";

it("useCounter oshadi va kamayadi", () => {
  const { result } = renderHook(() => useCounter(5));

  expect(result.current.count).toBe(5);                            // boshlang'ich

  act(() => result.current.increment());                          // state o'zgartirish — act
  expect(result.current.count).toBe(6);

  act(() => result.current.decrement());
  expect(result.current.count).toBe(5);
});

Misol 10 — Provider bilan test (Context — 2.4)

tsx
// Test uchun yordamchi — providerlar bilan render:
function renderWithProviders(ui: React.ReactElement) {
  return render(
    <QueryClientProvider client={new QueryClient()}>
      <AuthProvider>
        <BrowserRouter>{ui}</BrowserRouter>
      </AuthProvider>
    </QueryClientProvider>
  );
}

it("login bo'lgan foydalanuvchi profilni ko'radi", async () => {
  renderWithProviders(<Profile />);
  expect(await screen.findByText(/profil/i)).toBeInTheDocument();
});
//  Provider talab qiladigan komponent — yordamchi bilan o'ra (Context/Router/Query — 11.15)

Misol 11 — Shartli holatlar test (empty/error — 2.10)

tsx
it("bo'sh ro'yxatda empty holatni ko'rsatadi", async () => {
  server.use(http.get("/api/users", () => HttpResponse.json([])));   // bo'sh javob
  render(<UsersPage />);
  expect(await screen.findByText("Foydalanuvchilar yo'q")).toBeInTheDocument();
});

it("xato bo'lganda error holatni ko'rsatadi", async () => {
  server.use(http.get("/api/users", () => new HttpResponse(null, { status: 500 })));  // xato
  render(<UsersPage />);
  expect(await screen.findByText(/xato/i)).toBeInTheDocument();
});
//  Har holat (empty/error/data) alohida test (11.15: 2.8 — to'liq qamrov)

Misol 12 — Playwright e2e: login oqimi (2.9)

ts
import { test, expect } from "@playwright/test";

test("foydalanuvchi login qilib dashboard'ga o'tadi", async ({ page }) => {
  await page.goto("/login");

  await page.getByLabel("Email").fill("admin@x.uz");
  await page.getByLabel("Parol").fill("12345678");
  await page.getByRole("button", { name: "Kirish" }).click();

  await expect(page).toHaveURL("/dashboard");                      // o'tdi
  await expect(page.getByRole("heading", { name: /dashboard/i })).toBeVisible();
});

test("noto'g'ri parol bilan xato ko'rsatiladi", async ({ page }) => {
  await page.goto("/login");
  await page.getByLabel("Email").fill("admin@x.uz");
  await page.getByLabel("Parol").fill("wrong");
  await page.getByRole("button", { name: "Kirish" }).click();
  await expect(page.getByText(/noto'g'ri/i)).toBeVisible();
});

Misol 13 — Playwright e2e: CRUD oqimi (2.9)

ts
test("mahsulot qo'shadi va ro'yxatda ko'radi", async ({ page }) => {
  await page.goto("/products");
  await page.getByRole("link", { name: "+ Yangi" }).click();

  await page.getByLabel("Nom").fill("Yangi mahsulot");
  await page.getByLabel("Narx").fill("50000");
  await page.getByRole("button", { name: "Qo'shish" }).click();

  await expect(page).toHaveURL("/products");
  await expect(page.getByText("Yangi mahsulot")).toBeVisible();     // ro'yxatda paydo bo'ldi
});
//  Butun CRUD oqimi real brauzerda (yaratish  ro'yxatda ko'rish — 2.9)

Misol 14 — Test strategiyasi misoli (2.10)

tsx
//  TEST QIL — murakkab mantiq (validatsiya):
it("parol kuchini to'g'ri baholaydi", () => {
  expect(checkPasswordStrength("12345678")).toBe("zaif");
  expect(checkPasswordStrength("Abc123!@#")).toBe("kuchli");
});

//  TEST QIL — foydalanuvchi oqimi (forma):
it("forma to'g'ri ma'lumot bilan yuboriladi", async () => { /* userEvent ... */ });

//  TEST QIL — regression (tuzatilgan bug):
it("0 narxli mahsulotni qabul qiladi (bug #42)", () => {
  expect(calculateTotal([{ price: 0, qty: 5 }])).toBe(0);          // ilgari xato berardi
});

//  TEST QILMA — oddiy ko'rsatish (faqat props chizadi):
// function Logo() { return <img src="/logo.png" />; }  — sinashga arzimadi (2.10)

Misol 15 — Router test (MemoryRouter — 2.12)

tsx
import { render, screen } from "@testing-library/react";
import { MemoryRouter, Routes, Route } from "react-router-dom";
import { UserPage } from "./UserPage";

it("URL parametriga qarab to'g'ri foydalanuvchini ko'rsatadi", () => {
  render(
    <MemoryRouter initialEntries={["/users/5"]}>       {/* boshlang'ich yo'l */}
      <Routes>
        <Route path="/users/:id" element={<UserPage />} />
      </Routes>
    </MemoryRouter>
  );

  expect(screen.getByRole("heading", { name: /foydalanuvchi 5/i })).toBeInTheDocument();
});
//  MemoryRouter — brauzersiz routing test; useParams/Link/useNavigate sinaladi (11.9)

Misol 16 — Fake timer: debounce hook (2.11)

tsx
import { renderHook, act } from "@testing-library/react";
import { vi } from "vitest";
import { useDebounce } from "./useDebounce";

it("qiymatni kechiktirib yangilaydi (debounce)", () => {
  vi.useFakeTimers();                                  // vaqtni boshqaramiz
  const { result, rerender } = renderHook(({ v }) => useDebounce(v, 500), {
    initialProps: { v: "a" },
  });

  rerender({ v: "ab" });                               // qiymat o'zgardi
  expect(result.current).toBe("a");                    // hali eski (500ms o'tmadi)

  act(() => vi.advanceTimersByTime(500));              // 500ms "o'tkazamiz"
  expect(result.current).toBe("ab");                   // endi yangilandi

  vi.useRealTimers();                                  // real vaqtni qaytaramiz
});
//  Fake timer — debounce/setTimeout'ni real kutmasdan, tez va deterministik sinaydi (2.11)

Misol 17 — Accessibility test (jest-axe — 2.13)

tsx
import { render } from "@testing-library/react";
import { axe } from "jest-axe";
import { LoginForm } from "./LoginForm";

it("a11y buzilishlari yo'q", async () => {
  const { container } = render(<LoginForm />);
  const results = await axe(container);
  expect(results).toHaveNoViolations();                // label, kontrast, ARIA — avtomatik
});
//  jest-axe — a11y'ni avtomatik tekshiradi 1.9-bob; getByRole test = yaxshi a11y (2.4)

Misol 18 — Playwright: auth setup va within (2.14)

ts
// auth.setup.ts — bir marta login qilib holatni saqlaydi (keyingi testlar qayta ishlatadi):
import { test as setup } from "@playwright/test";

setup("login qilib holatni saqlaydi", async ({ page }) => {
  await page.goto("/login");
  await page.getByLabel("Email").fill("admin@x.uz");
  await page.getByLabel("Parol").fill("12345678");
  await page.getByRole("button", { name: "Kirish" }).click();
  await page.waitForURL("/dashboard");
  await page.context().storageState({ path: "playwright/.auth/user.json" });  // holat saqlandi
});

// keyingi test loginlangan holatda ishlaydi (auth.json orqali — playwright.config'da use):
test("ro'yxatdan bitta qatorni o'chiradi", async ({ page }) => {
  await page.goto("/users");
  const row = page.getByRole("row", { name: /Ali/ });          // kerakli qator
  await row.getByRole("button", { name: "O'chirish" }).click(); // shu QATOR ichida (within kabi)
  await expect(page.getByText("Ali")).toBeHidden();
});
//  storageState — har test qayta login qilmaydi (tez); qator ichida aniq amal (2.13, 2.14)

5. To'g'ri va noto'g'ri holatlar

1) Test falsafasi

text
 component.state.count tekshirish (implementatsiya — mo'rt — 2.3)
 screen.getByText("Hisob: 5") (foydalanuvchi ko'radigan — RTL)

2) Query tanlovi

text
 getByTestId hamma joyda (a11y'ni e'tiborsiz)
 getByRole/getByLabelText (foydalanuvchi/a11y kabi — 2.4)

3) Async ma'lumot

text
 getByText (fetch'dan keyin — element hali yo'q — xato — 2.6)
 await findByText (paydo bo'lishini kutadi)

4) Interaksiya

text
 fireEvent.click (past daraja)
 await userEvent.click (haqiqiy foydalanuvchi kabi — 2.5)

5) Coverage

text
 100% coverage maqsad (foydasiz test — vaqt isrofi — 2.10)
 muhim mantiq/oqim qamralsin (sifat > raqam)

6) E2e ko'lami

text
 hamma narsani e2e (sekin, mo'rt — 2.1)
 kritik yo'l e2e (login/to'lov); qolgani RTL integration

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — Unable to find an element with text... (async)

Sababi: getByText ishlatildi, lekin element keyinroq (fetch'dan) paydo bo'ladi 2.6-bob. Yechimi: await findByText (kutadi).

Xato 2 — not wrapped in act(...) ogohlantirishi

Sababi: state o'zgardi, lekin act/await yo'q 2.8-bob. Yechimi: userEventni await; hook state'ni act() ichida (Misol 9).

Xato 3 — Test komponent provider'siz ishlamaydi

Sababi: komponent Context/Router/Query talab qiladi 11.15-bob. Yechimi: renderWithProviders yordamchi (Misol 10).

Xato 4 — Mock API ishlamaydi (haqiqiy server'ga boradi)

Sababi: MSW server sozlanmagan yoki handler yo'q 2.7-bob. Yechimi: setupServer + beforeAll(server.listen()) (Misol 8).

Xato 5 — Test boshqa testga ta'sir qiladi (flaky)

Sababi: holat testlar orasida tozalanmagan. Yechimi: afterEach(cleanup) (RTL avtomatik), server.resetHandlers(), mock'larni tozala (vi.clearAllMocks()).

Xato 6 — getByText bir nechta element topadi (xato)

Sababi: matn bir nechta joyda. Yechimi: getAllByText (massiv), yoki aniqroq query (getByRole + name — 2.4).

Xato 7 — Playwright test mahalliy ishlaydi, CI'da yiqiladi

Sababi: vaqt (timing), tayyor bo'lmagan element. Yechimi: await expect(...).toBeVisible() (Playwright avtomatik kutadi — getByText darrov emas).

Xato 8 — Fake timer bilan test qotib qoladi yoki act ogohlantiradi

Sababi: vi.useFakeTimers() yoqilgan, lekin timer surilmagan (advanceTimersByTime yo'q) yoki useRealTimers() bilan qaytarilmagan 2.11-bob. Yechimi: timerni act(() => vi.advanceTimersByTime(ms)) bilan sur va test oxirida vi.useRealTimers() (Misol 16).


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • Komponent/hooks (11.2-11.7): RTL komponentni, renderHook hookni sinaydi.
  • Forma 11.10-bob: forma test (userEvent + validatsiya — Misol 4, 5).
  • Routing 11.9-bob: Playwright navigatsiya (e2e); RTL — BrowserRouter bilan.
  • Data fetching 12.4-bob: MSW bilan API mock (loading/error/empty — Misol 8, 11).
  • Custom hooks 11.7-bob: renderHook (mantiq sinash — 2.8).
  • a11y 1.9-bob: getByRole — a11y'ni rag'batlantiradi (yaxshi test = yaxshi a11y).
  • CI/CD 10.5-bob: testlar har push'da (GitHub Actions — npm test).
  • Backend test 8.11-bob: Jest unit/e2e — frontend RTL/Playwright bilan to'liq qamrov.

8. Eng yaxshi amaliyotlar (best practices)

  • Foydalanuvchi kabi sina (RTL — implementatsiya emas, xulq — 2.3).
  • getByRole/getByLabelText (a11y kabi; testId — oxirgi chora — 2.4).
  • userEvent (fireEvent emas) (haqiqiy interaksiya — await — 2.5).
  • findBy async ma'lumotga (getBy darrov — 2.6).
  • MSW API mock'ga (tarmoq darajasida — haqiqiyroq — 2.7).
  • renderHook custom hook'ga (mantiq izolyatsiya — 2.8).
  • E2e faqat kritik yo'lga (login/to'lov — kam, muhim — 2.9).
  • Muhim joyni test qil (mantiq/oqim/bug; 100% coverage emas — 2.10).
  • Bug avval test (regression — qayta kelmasin — 2.10).
  • Testni izolyatsiya qil (beforeEach + vi.clearAllMocks() — flaky yo'q — 2.11).
  • Fake timer taymer/debounce'ga (real vaqt kutmasdan — deterministik — 2.11).
  • within o'xshash elementlar orasida ("bir nechta topildi" xatosini yechadi — 2.13).
  • A11y'ni test qil (jest-axe — avtomatik tekshiruv — 2.13, 1.9).
  • Snapshot'ni ehtiyot bilan (mo'rt — xulq testi afzal — 2.12).
  • Playwright auto-wait'ga tayan (sleep yozma — flaky kamayadi — 2.14, Xato 7).
  • CI/CD'da testlar (har push — avtomatik — 10.5).

9. Amaliy loyiha: "Dashboard'ni To'liq Test Qilish"

11.15-bobdagi dashboard'ni (yoki istalgan loyihani) to'liq test bilan qoplash — ishonchli, professional kod.

Maqsad

Mavjud loyihaga (dashboard) Vitest + RTL + Playwright bilan test qatlami qo'sh — unit, integration, e2e.

Talablar (requirements)

  1. Setup: Vitest + RTL + jsdom + userEvent 2.2-bob.
  2. Unit: murakkab mantiq/util (calculateTotal, validatsiya — Misol 1, 14).
  3. Komponent: kamida 3 komponent render + xulq test (getByRole — Misol 2, 3).
  4. Forma: login/CRUD forma — to'ldirish + yuborish + validatsiya (Misol 4, 5).
  5. Async: ma'lumot yuklanishi (findBy — Misol 6); empty/error holatlar (Misol 11).
  6. Mock: vi.fn (callback) + MSW (API — Misol 7, 8).
  7. Custom hook: kamida bitta renderHook (Misol 9).
  8. Provider: renderWithProviders yordamchi (Context/Router/Query — Misol 10).
  9. E2e (Playwright): kritik oqim — login + CRUD (Misol 12, 13).
  10. Strategiya: har test NEGA yozilganini izohla (muhim joy — 2.10).

Maslahatlar (hint)

  • RTL — foydalanuvchi kabi (getByRole, userEvent — implementatsiya emas — 2.3).
  • Async — getBy emas, findBy (Xato 1).
  • API — MSW bilan mock (haqiqiy fetch + soxta javob — Misol 8).
  • Provider talab qiladigan komponent — renderWithProviders (Misol 10, Xato 3).
  • E2e faqat kritik yo'l (login, CRUD — hamma narsa emas — 2.1).
  • Bug topsangiz — avval test yoz (regression — 2.10).

"Tayyor" mezonlari (acceptance criteria)

  • Vitest + RTL sozlangan, testlar ishlaydi.
  • Unit test (mantiq/util).
  • Komponent test (getByRole, xulq — implementatsiya emas).
  • Forma test (to'ldirish + yuborish + validatsiya).
  • Async test (findBy + empty/error holatlar).
  • Mock (vi.fn + MSW).
  • Custom hook test (renderHook).
  • Provider yordamchi (Context/Router).
  • Playwright e2e (login + CRUD kritik oqim).
  • Har test oqlangan (muhim joy — coverage raqami emas).

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda kodga ishonch beruvchi qatlam — testni chuqur o'rgandik:

  • Nega test (ishonch, regression — 2.1); Vitest (anatomiya, AAA — 2.2); RTL falsafasi (foydalanuvchi kabi — 2.3).
  • render/screen/queries (getByRole — 2.4); userEvent 2.5-bob; async (findBy/waitFor — 2.6); mocking (vi.fn/MSW — 2.7); renderHook 2.8-bob.
  • Playwright e2e (real brauzer — 2.9); strategiya (nima test qilish — 2.10).

Endi siz ishonchli, professional kod yoza olasiz — refactoring va yangi feature qo'rqmasdan qilinadi (testlar buzilishni tutadi), bug regression yo'q, va kod "qanday ishlashi kerak"ligi test bilan hujjatlangan. Bu — junior va senior kodini ajratadigan muhim qatlam.

11-QISM (Frontend: React) TO'LIQ TUGADI! (11.1–11.17: DOM'dan React'ga, komponent, state, hooklar, custom hooks, code splitting, routing, forma, performance, ilg'or naqshlar, TypeScript, dashboard loyiha, animatsiya, test). Endi siz zamonaviy, professional React dasturchisisiz — interaktiv, tez, ishonchli, type-safe, jonli, sinalgan ilovalar qura olasiz.

Keyingi QISM — 12-QISM: State Management va Data Fetching. React'ni yana yuqori darajaga olib chiqamiz: Context API (chuqur), Redux Toolkit (global state — katta ilova), RTK Query va TanStack Query (server ma'lumotini professional boshqarish — kesh, sinxronlash, optimistic update), Zustand (yengil global state), va UI kutubxonalar (shadcn/ui, MUI). Bu — katta, real ilovalar uchun zarur — 11.15 dashboard'dagi ma'lumot va holat boshqaruvini professional darajaga ko'taradi.


Foydalanilgan rasmiy/ishonchli manbalar

  • Vitest rasmiy hujjati (vitest.dev) — describe/it/expect, matchers, vi.fn/vi.mock/vi.spyOn, fake timers, coverage konfiguratsiyasi
  • React Testing Library rasmiy hujjati (testing-library.com/react) — render/screen, queries prioriteti (getByRole...), userEvent, async (findBy/waitFor), within/rerender/cleanup, renderHook
  • Kent C. Dodds — "Testing Trophy", "Common mistakes with React Testing Library", "Write tests. Not too many. Mostly integration."
  • Playwright rasmiy hujjati (playwright.dev) — e2e, locator'lar, web-first assertion'lar (auto-wait), fixture, storageState bilan autentifikatsiya, trace/screenshot/video, parallel va CI
  • MSW rasmiy hujjati (mswjs.io) — tarmoq darajasida API mocking (http, HttpResponse, setupServer)
  • jest-dom (@testing-library/jest-dom) — DOM matcher'lari (toBeInTheDocument...); jest-axe — avtomatik accessibility tekshiruvi
  • React rasmiy hujjati (react.dev) — testing bo'yicha tavsiyalar; WAI-ARIA roles (getByRole — a11y asosi); jsdom — brauzer muhiti taqlidi

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
11.17-bob: Testing — Vitest, React Testing Library, Playwright — Wisar