Mundarija (32)
- Bu darsda
- 1. Nega bu kerak?
- 2. Kim nimani sinaydi
- 2.1 Taqsimot
- 2.2 Server Action qanday sinaladi
- 3. Vitest'ni Next uchun sozlash
- 3.1 O'rnatish
- 3.2 vitest.config.mts
- 3.3 Router mock'i
- 3.4 Jonli: client komponent testi
- 3.5 Biz ishga tushirdik: npm test
- 4. Async Server Component — Vitest chizmaydi
- 4.1 Kanon tajribasi
- 4.2 A: render(<KontentTiliIzohi />)
- 4.3 B: funksiya sifatida chaqirish — o'tdi
- 4.4 C: "use cache" — Next'siz ishlamaydi
- 4.5 Xulosa
- 5. Playwright: production build ustida
- 5.1 playwright.config.ts
- 5.2 15 ta test nimani tekshiradi
- 5.3 Biz ishga tushirdik: e2e va flaky test
- 6. MSW server tomonda (ko'rsatkich)
- 7. Portfolio qadami: testlar va commit (holat 41)
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): qaysi vosita?
- 2-mashq (o'rta): tilga qarab havola
- 3-mashq (qiyin): flaky'ni aniqlash
- 4-mashq: Portfolio qadami
- 10. Real ishda
- Xulosa
- Manbalar
Next.js ilovani test qilish: Vitest, Playwright va Server Component cheklovi
Qisqacha: Next.js ilovasi ikki qavatda sinaladi. Vitest + Testing Library — toza mantiq (Zod sxemasi, yordamchi funksiyalar) va client komponentlar; Next'ning router'i testda yo'q, shuning uchun
next/navigationvanext/linkmock qilinadi,server-onlyesa bo'sh modulga almashtiriladi. Playwright — butun ilova: production build (next build+next start) ustida haqiqiy brauzerda forma, modal, i18n, 404 statuslar va axe. Async Server Component'lar va"use cache"li funksiyalar Vitest'da chizilmaydi — kanon tajribasi buni uch variantda ko'rsatdi — shuning uchun server qismlari e2e bilan tekshiriladi.portfolioda: Vitest 10 ta, Playwright 15 ta test.
Bu darsda
- Next ilovasining qaysi qismini qaysi vosita bilan sinashni tanlay olasiz.
- Vitest'ni Next uchun sozlaysiz: jsdom,
@alias,server-onlyalmashtirish,next/navigationmock. - Client komponent testini yozasiz (natija oynasida
next/navigationo'quv modeli bilan, haqiqiy loyihadavi.mockbilan). - Async Server Component nega Vitest'da chizilmasligini haqiqiy xato matnlari bilan tushuntirasiz.
- Playwright'ni production build ustida ishga tushirasiz:
webServer, JS'siz test, axe, flaky testni aniqlash.
Oldin bilishingiz kerak: Playwright II: auth, tarmoq, trace va CI, Vitest va RTL asoslari, Server Actions II, Server va Client kompozitsiyasi, Env o'zgaruvchilar.
1. Nega bu kerak?
22-qismda vazifalar ga 300 dan ortiq test yozdingiz — Vitest, RTL, MSW, Playwright, Storybook. U SPA edi: hamma kod brauzerda, test uni jsdom'da yoki Chrome'da ochardi.
portfolio da kod uch joyda yashaydi: brauzerda (client komponentlar), Node serverda (Server Component, Server Action, Route Handler, proxy) va build vaqtida (statik sahifalar, "use cache"). Vitest'ning jsdom'i birinchisini yaxshi taqlid qiladi. Ikkinchisi va uchinchisi uchun unda Next'ning o'zi yo'q.
Savol: nimani qayerda sinaymiz? Kanon bunga tajriba bilan javob berdi.
O'xshatish: avtomobil. Detallarni (tormoz kolodkasi, chiroq) stendda alohida sinaysiz — tez va arzon. Lekin "mashina yuradimi" degan savolga faqat yo'lda haydab javob olasiz. Vitest — stend, Playwright — sinov yo'li.
2. Kim nimani sinaydi
2.1 Taqsimot
| Qism | Misol (portfolio) |
Vosita |
|---|---|---|
| Toza mantiq | tilYoli, oyniYoz, tezlikCheklovi |
Vitest |
| Zod sxemasi (Server Action'ning yuragi) | aloqa formasi tekshir() |
Vitest |
| Client komponent | TegFiltri, TemaTanlov, UlashishTugmasi |
Vitest + RTL |
| Server Component, Server Action butunligicha | sahifalar, xabarYubor |
Playwright |
proxy.ts, redirect, statuslar |
404, 308 | Playwright (request) |
| Kirish imkoniyati | 8 sahifa | Playwright + axe |
flowchart TB
M["toza mantiq, Zod"] --> V["Vitest<br/>10 test, ~2 s"]
C["client komponentlar"] --> V
S["Server Components,<br/>Server Actions, proxy"] --> P["Playwright<br/>15 test, prod build"]
B["build: statik, use cache"] --> PChap tomondagi narsa qanchalik "serverga yaqin" bo'lsa, shuncha o'ngga — e2e'ga suriladi.
2.2 Server Action qanday sinaladi
Server Action — oddiy async funksiya, lekin ichida headers(), after() kabi Next API'lari bor (Request API'lar). Ularni Vitest'da chaqirsangiz — so'rov konteksti yo'q. Kanon yo'li: action'ni ikkiga bo'lish. Mantiq (tekshir() — Zod sxemasi, xatolarni tarjima kalitlariga aylantirish) — alohida, Next'siz funksiya — Vitest'da sinaladi. Ulash qismi (so'rov, IP, javob) — e2e'da. Server Actions II da sxemani alohida faylga chiqarishning bir sababi shu edi.
3. Vitest'ni Next uchun sozlash
3.1 O'rnatish
Kanon 22-qism kanonidagi aniq versiyalarni oldi (--save-exact): vitest 5.0.4, @vitejs/plugin-react 6.1.2, jsdom 30.1.2, @testing-library/react 16.3.3, dom 10.4.2, user-event 14.6.8, jest-dom 7.0.1. Ular faqat devDependencies da — brauzer JS'iga kirmaydi (bosh sahifa 149.6 KB — o'zgarmadi).
3.2 vitest.config.mts
import { fileURLToPath } from "node:url";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
"server-only": fileURLToPath(
new URL("./tekshiruv/bosh-modul.ts", import.meta.url),
),
},
},
test: {
environment: "jsdom",
include: ["tekshiruv/**/*.test.{ts,tsx}"],
setupFiles: ["tekshiruv/sozlash.ts"],
},
});Ikki Next'ga xos qator:
| Qator | Nega |
|---|---|
"@": …/src |
Loyiha @/komponentlar/… import qiladi; Next buni tsconfig dan o'qiydi, Vitest'ga alohida aytiladi |
"server-only": …/bosh-modul.ts |
Asl server-only paketi React server sharti bo'lmasa xato tashlaydi — test muhitida bo'sh fayl bilan almashtiriladi |
bosh-modul.ts ichida bitta qator: export {};. Env darsida server-only build'da qanday himoya qilganini ko'rgansiz; testda esa u to'siq bo'lmasligi kerak — chunki biz serverga tegishli mantiqni sinaymiz.
sozlash.ts — 22-qismdan tanish uch narsa: jest-dom matcherlarini ulash (Vitest 5 tur aylanma yo'li bilan — RTL I), har testdan keyin cleanup(), IS_REACT_ACT_ENVIRONMENT = true.
3.3 Router mock'i
TegFiltri useSearchParams va usePathname ni ishlatadi. Testda Next router'i yo'q — bu hooklar xato beradi. Kanon ularni vi.mock bilan almashtirdi (vi.mock):
const url = vi.hoisted(() => ({ yol: "/en/loyihalar", qidiruv: "" }));
vi.mock("next/navigation", () => ({
useParams: () => ({ locale: url.yol.startsWith("/en") ? "en" : "uz" }),
usePathname: () => url.yol,
useSearchParams: () => new URLSearchParams(url.qidiruv),
}));
// next/link App Router'siz ishlamaydi — oddiy <a> bilan
type LinkProps = { href: string; children: ReactNode };
vi.mock("next/link", () => ({
default: ({ href, children }: LinkProps) => (
<a href={href}>{children}</a>
),
}));vi.hoisted — url obyektini vi.mock dan oldin yaratadi (vi.mock fayl boshiga ko'tariladi). Har test url.qidiruv = "teg=HTML" deb o'z holatini beradi. Kanondagi next/link mock'i aria-current ni ham uzatadi; bu yerda qisqartirildi (kanonda 4 qatorli tur).
3.4 Jonli: client komponent testi
Natija oynasida vi.mock yo'q — lekin unga kerak ham emas: next/navigation va next/link bu yerda allaqachon o'quv modeli (sahifa boshidagi ih.boshla — model sozlamasi, Next API'si emas). Kanon TegFiltri ning qisqa nusxasi:
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import Link from "next/link";
import { ih, usePathname, useSearchParams } from "next/navigation";
type Item = { key: string; tags: string[]; title: string };
function TagFilter({ items }: { items: Item[] }) {
const tag = useSearchParams().get("teg");
const path = usePathname();
const visible = tag
? items.filter((i) => i.tags.includes(tag))
: items;
const tags = [...new Set(items.flatMap((i) => i.tags))].sort();
return (
<>
{tags.map((t) => (
<Link key={t} href={`${path}?teg=${t}`}>
{t}
</Link>
))}
<p role="status">{visible.length} ta loyiha ko'rsatilmoqda</p>
</>
);
}
const ITEMS: Item[] = [
{ key: "a", tags: ["React", "TS"], title: "Vazifalar" },
{ key: "b", tags: ["HTML"], title: "Bahor" },
];
describe("TagFilter", () => {
it("?teg=HTML — faqat mos loyiha", () => {
ih.boshla("/loyihalar?teg=HTML");
render(<TagFilter items={ITEMS} />);
const status = screen.getByRole("status");
expect(status).toHaveTextContent("1 ta loyiha");
});
it("havolalar joriy yo'l bilan", () => {
ih.boshla("/loyihalar");
render(<TagFilter items={ITEMS} />);
const react = screen.getByRole("link", { name: "React" });
expect(react).toHaveAttribute("href", "/loyihalar?teg=React");
});
});Konsolda:
✓ TagFilter > ?teg=HTML — faqat mos loyiha
✓ TagFilter > havolalar joriy yo'l bilan
Testlar: 2 o'tdi (jami 2)Natija oynasidagi vitest — 22-qismdagi o'quv modeli, next/navigation — 23-qismdagi model; haqiqiy chiqish terminalda (pastda). Haqiqiy loyihada ih.boshla o'rniga yuqoridagi vi.mock va url obyekti ishlaydi — g'oya bir xil: testda URL holatini o'zingiz berasiz.
3.5 Biz ishga tushirdik: npm test
portfolio (holat 43 nusxasi), AI_AGENT va CLAUDECODE o'chirilgan (aks holda Vitest 5 boshqa hisobot formatiga o'tadi — 22-qism):
RUN v5.0.4 C:/Users/…/p
✓ tekshiruv/birlik/mantiq.test.ts (6 tests) 30ms
✓ tekshiruv/birlik/komponentlar.test.tsx (4 tests) 1262ms
✓ TegFiltri (2)
✓ filtrsiz — hammasi, «All» joriy 709ms
✓ TemaTanlov (1)
✓ Qorong'i tanlansa — data-tema va localStorage 364ms
Test Files 2 passed (2)
Tests 10 passed (10)
Start at 12:42:39
Duration 17.49s (environment 87%, setup 6%, tests 4%, import 2%, transform 1%)10 test. Vitest faqat sekin (300 ms dan uzoq) testlarni alohida qatorda ko'rsatadi — qolganlari fayl qatorida sanalgan. Duration 17.5 s, shundan 87% — jsdom muhitini ko'tarish; testlarning o'zi 1.3 s. Birinchi (sovuq) ishga tushishda 82.6 s ketdi. Kanon issiq holatda 1.96 s o'lchagan — sizning kompyuteringizda va antivirusingizga qarab raqam juda farq qiladi.
Tekshirib ko'ring: Nega
TegFiltritestidanext/linkni ham mock qilish kerak bo'ldi, faqatnext/navigationyetmadimi?
Javob
next/link ichida App Router konteksti (router, prefetch) ishlatiladi. Testda Next router'i yo'q, <Link> uni topa olmaydi. Bizga <Link> ning faqat bitta xususiyati kerak — to'g'ri href — shuning uchun oddiy <a> yetarli. Natija oynasida next/link allaqachon shunday model.
4. Async Server Component — Vitest chizmaydi
4.1 Kanon tajribasi
KontentTiliIzohi — portfolio dagi kichik async Server Component: /en sahifalarida "bu matn o'zbekcha" degan izoh (next-intl). Kanon uni uch yo'l bilan sinab ko'rdi (xom chiqish, qisqartirilgan):
❯ tekshiruv/birlik/async-sinov.test.tsx (3 tests | 2 failed) 1052ms
× A) async Server Component'ni RTL bilan to'g'ridan chizish 1037ms
✓ B) funksiya sifatida await qilib, natijani chizish 12ms
× C) 'use cache' li funksiya (postlar) Vitest'da 2ms4.2 A: render(<KontentTiliIzohi />)
React ogohlantirishi:
<KontentTiliIzohi> is an async Client Component. Only Server Components can be async at the moment. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server.Tarjimasi: "<KontentTiliIzohi> — async Client Component. Hozircha faqat Server Component'lar async bo'la oladi". RTL komponentni brauzer React'i bilan chizadi — u yerda har komponent "client". Async komponent esa faqat server React'ida (RSC) ishlaydi. Ikkinchi ogohlantirish — act ni await qilmaslik haqida — va test matnni topa olmay yiqildi.
4.3 B: funksiya sifatida chaqirish — o'tdi
render(await KontentTiliIzohi());Server Component — aslida JSX qaytaradigan async funksiya. Uni o'zingiz await qilib chaqirsangiz, natija oddiy JSX — RTL uni chizadi. Kanonda bu ishladi, lekin ikki shart bilan: ichidagi Next API'lari (next/root-params, next-intl/server) mock qilindi, ichida Suspense va hook yo'q. Murakkabroq komponentda bu yo'l tez buziladi.
4.4 C: "use cache" — Next'siz ishlamaydi
Error: `cacheLife()` is only available with the `cacheComponents` config.postlar() (blog postlari ro'yxati) "use cache" va cacheLife("max") bilan yozilgan (Cache Components I). Bu direktiva va funksiyalarni Next build'i va serveri bajaradi. Vitest uchun ular — Next sozlamasi yo'q joydagi oddiy chaqiruv.
4.5 Xulosa
Next hujjati ham shuni aytadi: async Server Component'larni Vitest qo'llamaydi, ular uchun e2e tavsiya qilinadi. Kanon qarori — server qismlari Playwright'da. Unit testlar uchun esa ulardagi mantiqni toza funksiyaga chiqaring (oyniYoz, tekshir) — u Next'siz sinaladi.
5. Playwright: production build ustida
5.1 playwright.config.ts
import { defineConfig } from "@playwright/test";
const PORT = 3200;
const CI = process.env["CI"] !== undefined;
export default defineConfig({
testDir: "e2e",
fullyParallel: true,
forbidOnly: CI,
retries: CI ? 2 : 0,
reporter: [["list"]],
use: {
baseURL: `http://localhost:${PORT}`,
channel: "chrome",
trace: "on-first-retry",
},
webServer: {
command: `npm run start -- -p ${PORT}`,
url: `http://localhost:${PORT}/icon.svg`,
reuseExistingServer: !CI,
timeout: 60_000,
},
});Eng muhim qator — webServer.command: next start, next dev emas. Testlar production build ustida ishlaydi: o'quvchi ko'radigan aynan o'sha sahifalar, o'sha kesh, o'sha xato yashirish. Shuning uchun avval npm run build, keyin npm run test:e2e. url — server tayyorligini tekshirish manzili (kichik statik fayl). channel: "chrome" — tizimdagi Chrome (22-qism qarori), alohida brauzer yuklanmaydi.
5.2 15 ta test nimani tekshiradi
| Guruh | Nima | Nega e2e |
|---|---|---|
| Bosh sahifa | <h1>, joriy menyu, JSON-LD |
Server Component natijasi |
| Filtr | ?teg=React → 1 ta, «Hammasi» → 3 ta |
URL + client komponent |
| Modal | ushlovchi marshrut (intercepting route), Esc, to'g'ridan kirish | Parallel va intercepting — faqat Next'da |
| Forma (JS bilan) | server xatolari, aria-invalid, yuborish |
Server Action |
| Forma (JS'siz) | yuborish ishlaydi | Progressive enhancement |
| i18n | almashtirgich, lang="en", inglizcha xatolar |
next-intl serverda |
| Statuslar va SEO | 4 ta 404, RSS, sitemap hreflang, 308 | proxy.ts, next.config |
| axe | 8 sahifa (uz/en) | Haqiqiy brauzer DOM'i |
JS'siz test — Playwright'ning bitta sozlamasi:
test.describe("JavaScript'siz", () => {
test.use({ javaScriptEnabled: false });
test("aloqa formasi ishlaydi", async ({ page }) => {
await page.goto("/aloqa");
await page.getByLabel("Ismingiz").fill("Malika");
await page.getByLabel("Email").fill("malika@example.com");
await page.getByLabel("Xabar").fill("JS o'chiq brauzerdan xabar.");
await page.getByRole("button", { name: "Yuborish" }).click();
await expect(page.getByText(/Rahmat, Malika!/)).toBeVisible();
});
});Server Action oddiy HTML forma sifatida ham ishlaydi — bu test buni har safar isbotlaydi. SPA'da bunday test umuman mumkin emas edi.
Yana bitta detal: har test o'z x-forwarded-for sarlavhasini yuboradi. Aloqa formasida tezlik cheklovi bor (IP boshiga 10 daqiqada 3 ta) — testlar bitta "IP" dan kelsa, to'rtinchi forma testi cheklovga uriladi va testlar bir-biriga bog'lanib qoladi.
5.3 Biz ishga tushirdik: e2e va flaky test
Birinchi ishga tushirish (build'dan keyin):
1 failed
e2e\sayt.spec.ts:42:5 › loyiha rasmi modal'da (intercepting route), Esc — orqaga
14 passed (30.6s)Xato:
Expected pattern: /\/loyihalar\/vazifalar\/rasm$/
Received string: "http://localhost:3200/loyihalar/vazifalar"
Timeout: 5000msRasm havolasi bosildi, lekin 5 soniya ichida manzil o'zgarmadi. Hech narsani o'zgartirmasdan yana uch marta ishga tushirdik:
## e2e 1
15 passed (30.3s)
## e2e 2
1 failed
e2esayt.spec.ts:42:5 › loyiha rasmi modal'da (intercepting route), Esc — orqaga
14 passed (31.0s)
## e2e 3
15 passed (27.2s)To'rt urinishdan ikkitasida bitta test — modal — yiqildi. Kanon esa 15/15 ni ketma-ket 3 marta olgan (8.5–8.7 s, flaky 0). Bu — flaky (beqaror) test belgisi: kod bir xil, natija har xil. Farqqa qarang: bizning urinishlar 27–31 s davom etdi, kanonniki 9 s — kompyuter boshqa ishlar bilan band edi. Ehtimoliy sabab: yuk ostida sahifa hydration'ni kech tugatadi (Hydration), bosish client navigatsiya tayyor bo'lmasdan sodir bo'ladi va modal manzili 5 soniyaga sig'maydi. Sababni aniq isbotlamadik — bu taxmin. Kanon tinch mashinada buni ko'rmagan, biz yuk ostida topdik.
Nima qilish kerak? Uch variant: (1) CI'da retries: 2 — kanon konfigida bor, Playwright flaky'ni "flaky" deb belgilaydi; (2) testda bosishdan oldin interaktivlikni kutish (masalan sahifa tayyorligini bildiradigan element); (3) sababni topish. Retry — bandaj, sabab — davo. Playwright II darsidagi trace (trace: "on-first-retry") aynan shu holat uchun: qayta urinishda Playwright har qadamni yozib oladi. Flaky testlar bilan ishlash tartibini Test strategiyasi va flaky testlar darsida ko'rgansiz.
Halol eslatma: kanon testi va kanon kodi o'zgartirilmadi — kanon o'z o'lchovida (tinch mashinada) 3/3 marta toza o'tgan. Bu topilma keyingi ish ro'yxatiga yozildi: modal testida bosishdan oldin sahifaning tayyorligini kutish.
6. MSW server tomonda (ko'rsatkich)
22-qismda MSW brauzer so'rovlarini ushladi. Next'da tashqi API'ga so'rov server dan ketadi (portfolio da GitHub API — malumot/github.ts). MSW'ning Node rejimi (setupServer) Vitest'da server fetch ni ushlaydi — DAL funksiyasini soxta javob bilan sinash mumkin. Lekin githubProfil() "use cache" bilan yozilgan — «Async Server Component» bo'limidagi C variant: Vitest'da ishlamaydi. Kanon boshqa yo'lni tanladi: GITHUB_API_URL env'i (Env) — e2e'da ishlamaydigan manzil berilsa, zaxira UI ko'rinadi. MSW Next'da kanonda ishlatilmagan.
7. Portfolio qadami: testlar va commit (holat 41)
| Fayl | Nima |
|---|---|
vitest.config.mts |
jsdom, @ alias, server-only → bo'sh modul |
tekshiruv/sozlash.ts, bosh-modul.ts |
jest-dom, cleanup, act muhiti; export {} |
tekshiruv/birlik/mantiq.test.ts |
6 test: oyniYoz, tilYoli, tezlikCheklovi (soxta soat), Zod sxemasi |
tekshiruv/birlik/komponentlar.test.tsx |
4 test: TegFiltri, TemaTanlov + TemaProvider, UlashishTugmasi |
playwright.config.ts, e2e/sayt.spec.ts |
15 test, production build ustida |
package.json |
test, test:kuzat, test:e2e; check = lint + tip + format + test |
Biz topdik (kanon): @axe-core/playwright o'z peer'i playwright-core ni npm orqali 1.64.1 bilan o'rnatdi (7 kundan yangi versiya). Natija — ikki nusxa, Page turlari mos kelmadi: tsc TS2322 va next build yiqildi (u e2e fayllarni ham tur tekshiradi). Yechim: playwright-core ni aniq 1.64.0 bilan devDependencies ga qo'shish (bitta nusxa qoladi) va eski .next/cache/.tsbuildinfo ni o'chirish.
Kanon natijasi: Vitest 10/10 (1.96 s), Playwright 15/15 (9.2 s); JS o'zgarmadi, axe 0, hydration 0. .gitignore ga test-results va playwright-report.
git add -A
git commitCommit (kanon): Test: Vitest + RTL (mantiq, client komponentlar), Playwright e2e production build ustida + axe.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
This module cannot be imported from a Client Component module testda |
Asl server-only |
Vitest'da alias — bo'sh modul |
useRouter() testda: invariant expected app router to be mounted |
Router konteksti yo'q (matn Next manba kodidan) | vi.mock("next/navigation") |
… is an async Client Component |
Async Server Component RTL bilan chizildi | E2E'da sinang yoki mantiqni ajrating |
cacheLife() is only available with the cacheComponents config |
"use cache" li funksiya Vitest'da |
E2E |
| E2E dev serverda o'tadi, prod'da yiqiladi | webServer next dev ni ishlatgan |
next start + oldin build |
| Forma testi to'rtinchi safar yiqiladi | Tezlik cheklovi — testlar bir IP'dan | Har testga o'z x-forwarded-for |
| Ba'zan o'tadi, ba'zan yo'q | Flaky — vaqt yoki hydration | Trace bilan sababni toping; CI'da retry |
TS2322 Page turlari, build yiqiladi |
playwright-core ikki nusxa |
Aniq versiya bilan devDependencies |
9. Mashqlar
Mashqlar kurs/mashqlar/23/41-test/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): qaysi vosita?
tilYoli("en", "/blog")natijasi —- Aloqa formasi JS o'chiq brauzerda yuboriladimi —
- Async Server Component
KontentTiliIzohining matni — TemaTanlovbosilgandadata-temao'zgaradimi —/loyihalar/yoqstatusi 404 mi —
Yechim
- Vitest — toza funksiya.
- Playwright — Server Action va haqiqiy HTML forma.
- Playwright — Vitest async Server Component'ni chizmaydi (tajriba A).
- Vitest — client komponent, jsdom yetadi.
- Playwright — status
proxy.tsva server javobidan keladi (request.get).
2-mashq (o'rta): tilga qarab havola
«Client komponent testi» blokidagi TagFilter ga til prefiksini qo'shing: useParams() dan locale olinadi, en bo'lsa havolalar /en bilan boshlanadi. Natija oynasida ih.boshla("/en/loyihalar", { locale: "en" }) bilan sinang: React havolasi /en/loyihalar?teg=React bo'lishi kerak. usePathname() /en/loyihalar ni qaytaradi — prefiksni ikki marta qo'shmang.
Yechim
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import Link from "next/link";
import { ih, useParams, usePathname } from "next/navigation";
function withLocale(locale: string, path: string) {
const bare = path.replace(/^\/en(?=\/|$)/, "") || "/";
return locale === "en" ? `/en${bare === "/" ? "" : bare}` : bare;
}
function TagLinks({ tags }: { tags: string[] }) {
const { locale = "uz" } = useParams<{ locale?: string }>();
const path = withLocale(locale, usePathname());
return (
<>
{tags.map((t) => (
<Link key={t} href={`${path}?teg=${t}`}>
{t}
</Link>
))}
</>
);
}
describe("TagLinks", () => {
it("en — prefiks bir marta", () => {
ih.boshla("/en/loyihalar", { locale: "en" });
render(<TagLinks tags={["React"]} />);
const link = screen.getByRole("link", { name: "React" });
expect(link).toHaveAttribute("href", "/en/loyihalar?teg=React");
});
it("uz — prefikssiz", () => {
ih.boshla("/loyihalar", { locale: "uz" });
render(<TagLinks tags={["React"]} />);
const link = screen.getByRole("link", { name: "React" });
expect(link).toHaveAttribute("href", "/loyihalar?teg=React");
});
});Konsolda:
✓ TagLinks > en — prefiks bir marta
✓ TagLinks > uz — prefikssiz
Testlar: 2 o'tdi (jami 2)withLocale avval mavjud /en ni olib tashlaydi, keyin kerak bo'lsa qo'shadi — shuning uchun /en/en/… bo'lmaydi. Kanonda bu ish ikkiga bo'lingan: usePathname o'rami (/en ni olib tashlaydi) va tilYoli (qo'shadi) — va tilYoli alohida Vitest testida sinalgan. (?=\/|$) — oldinga qarash (Lookahead): /en dan keyin / yoki satr oxiri, /english emas.
3-mashq (qiyin): flaky'ni aniqlash
Playwright natijalari tarixidan beqaror testlarni topadigan funksiya yozing: findFlaky(runs) — runs — har ishga tushirish uchun { testNomi: "passed" | "failed" } obyektlari massivi. Test kamida bir marta o'tgan va kamida bir marta yiqilgan bo'lsa — flaky. Har biri uchun "<nom>: 2/3 o'tdi" ko'rinishida chiqaring; flaky yo'q bo'lsa — "flaky yo'q". Bizning to'rt ishga tushirishimizdagi modal testi bilan sinang.
Yechim
function findFlaky(runs) {
const stats = new Map();
for (const run of runs) {
for (const [name, status] of Object.entries(run)) {
const s = stats.get(name) ?? { passed: 0, total: 0 };
s.total += 1;
if (status === "passed") s.passed += 1;
stats.set(name, s);
}
}
const flaky = [...stats]
.filter(([, s]) => s.passed > 0 && s.passed < s.total)
.map(([name, s]) => `${name}: ${s.passed}/${s.total} o'tdi`);
return flaky.length ? flaky : ["flaky yo'q"];
}
const modal = "loyiha rasmi modal'da";
const runs = [
{ [modal]: "failed", "bosh sahifa": "passed" },
{ [modal]: "passed", "bosh sahifa": "passed" },
{ [modal]: "failed", "bosh sahifa": "passed" },
{ [modal]: "passed", "bosh sahifa": "passed" },
];
for (const line of findFlaky(runs)) console.log(line);Konsolda:
loyiha rasmi modal'da: 2/4 o'tdi"bosh sahifa" har safar o'tdi — flaky emas. Hech qachon o'tmagan test ham flaky emas — u shunchaki buzuq. Katta jamoalarda aynan shunday hisobot CI tarixidan avtomatik tuziladi va flaky testlar alohida ro'yxatga olinadi.
4-mashq: Portfolio qadami
O'z portfolio ingizga Vitest (config, sozlash.ts, server-only alias) va kamida ikki test faylini qo'shing: mantiq (tilYoli yoki o'xshash) va bitta client komponent (vi.mock("next/navigation") bilan). Keyin Playwright: webServer — next start, kamida uchta e2e test (bosh sahifa, forma JS'siz, bitta 404 status). npm run build && npm run test:e2e ni uch marta ishga tushiring — har safar natija bir xilmi? npm run check toza bo'lsa — commit.
Yechim
Namuna — «Portfolio qadami» jadvali va yuqoridagi konfiglar. Uch marta ishga tushirishning maqsadi — flaky'ni erta ushlash: bitta yiqilish ham yozib qo'yilsin (qaysi test, qaysi qadamda). Agar next build e2e fayllardagi tur xatosi bilan yiqilsa — npm ls playwright-core bilan nusxalarni tekshiring (kanon topilmasi). AI agent ichida terminal ochsangiz, testlarni AI_AGENT/CLAUDECODE siz ishga tushiring — Vitest hisoboti o'zgaradi.
10. Real ishda
- Test piramidasi Next'da — ko'p unit (mantiq, client), kamroq lekin muhim e2e (server oqimlari). Jamoalar ko'pincha e2e'ni preview deploy ustida ham yurgizadi (Deploy I: Vercel).
- "Server Component'ni qanday test qilasiz?" — mashhur intervyu savoli. Javob: Vitest async RSC'ni chizmaydi; mantiqni toza funksiyaga ajratib unit test, butunligicha — Playwright.
- Flaky testlar — katta loyihalarning eng qimmat muammolaridan: ishonch yo'qoladi, "qayta ishga tushir" odatiy bo'lib qoladi. Ularni ro'yxatga olish va sababini tuzatish — muhandislik madaniyati.
- Progressive enhancement testi (
javaScriptEnabled: false) — kam jamoa qiladi, lekin u Server Action'ning asosiy afzalligini himoya qiladi.
Xulosa
- Mantiq va client komponentlar — Vitest + RTL; server qismlari va butun oqim — Playwright production build ustida.
- Next uchun Vitest:
@alias,server-only→ bo'sh modul,next/navigationvanext/linkmock (vi.hoistedbilan URL holati). - Async Server Component RTL'da chizilmaydi,
"use cache"Vitest'da ishlamaydi; funksiya sifatidaawaitqilish faqat oddiy holatda. - Playwright:
webServer—next start, har testga o'z IP, JS'siz forma testi, axe 8 sahifada. - Biz topdik: yuk ostida modal testi 4 urinishdan 2 tasida yiqildi (flaky); kanon topdi:
playwright-coreikki nusxasi build'ni buzdi.
Keyingi dars: Versiyalarni yangilash va Pages Router merosi — codemod bilan Next 15 → 16, va eski pages/ loyihalarini o'qish.
Manbalar
- Next.js hujjati — Testing: Vitest, Playwright (async Server Component cheklovi): https://nextjs.org/docs/app/guides/testing (16.4.0 bilan kelgan nusxa, 2026-10-21 da o'qildi)
- Vitest hujjati —
vi.mock,vi.hoisted: https://vitest.dev/api/vi - Playwright hujjati —
webServer,javaScriptEnabled, retries va trace: https://playwright.dev/docs/test-webserver - Kanon:
portfoliov2, holat 41 (konfiglar, testlar, async RSC tajribasi,playwright-coretopilmasi);npm testva e2e ishga tushirishlari (flaky modal) — biz ishga tushirdik (Vitest 5.0.4, Playwright 1.64.0, Chrome 155, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!