Mundarija (37)
- Bu darsda
- 1. Nega bu kerak?
- 2. Browser Mode qanday ishlaydi
- 2.1 To'rt ishtirokchi
- 2.2 Uch provayder
- 3. O'rnatish va sozlash
- 3.1 Ikki paket
- 3.2 vitest.config.ts — to'rtinchi loyiha
- 3.3 Nega npm test ga kirmadi
- 4. Birinchi brauzer testi
- 5. Lokatorlar va expect.element
- 5.1 Lokator — element emas, "qidiruv yo'riqnomasi"
- 5.2 expect.element — qayta urinadigan tekshiruv
- 5.3 Yashirin elementlar
- 6. userEvent: haqiqiy hodisalar
- 7. jsdom va haqiqiy brauzer: nima farq qiladi
- 7.1 O'lchov va joylashuv
- 7.2 Farqlar jadvali
- 7.3 Narx: vaqt
- 8. vitest-browser-react va MSW brauzerda
- 8.1 vitest-browser-react
- 8.2 MSW: endi haqiqiy service worker
- 9. Vizual tekshiruv: toMatchScreenshot (qisqa)
- 10. Vazifalar qadami: Browser Mode (holat 08)
- 10.1 Branch va o'rnatish
- 10.2 Testlar — tekshiruv/dialoglar.brauzer.tsx (6)
- 10.3 Uch tur tuzog'i (o'lchandi)
- 10.4 Natija va commit
- 11. Ko'p uchraydigan xatolar
- 12. Mashqlar
- 1-mashq (oson): bo'sh joylarni to'ldiring
- 2-mashq (o'rta): fokus qaytadimi?
- 3-mashq (qiyin): mobil menyu
- 4-mashq: Vazifalar qadami
- 13. Real ishda
- Xulosa
- Manbalar
Vitest Browser Mode: komponent testini haqiqiy Chrome'da yurgizish — lokatorlar, expect.element va jsdom farqi
Qisqacha: Vitest Browser Mode test faylini jsdom'da emas, haqiqiy brauzerda (Chrome, Firefox, WebKit) ishga tushiradi. Brauzerni provayder ochadi — odatda Playwright:
browser: { enabled: true, provider: playwright(), instances: [{ browser: "chromium" }] }. Elementlarpage.getByRole(...)lokatori bilan topiladi, tekshiruvawait expect.element(lokator).toBeVisible()— shart bajarilguncha qayta uriniladi.userEvent(vitest/browser) haqiqiy klaviatura va sichqoncha hodisalarini yuboradi. Narxi — tezlik: bitta test jsdom'dagidan taxminan 6 baravar sekin (holat 08 o'lchovi). Shuning uchun Browser Mode'ga faqat jsdom ko'rmaydigan narsalar o'tadi: modal dialog, fokus, o'lcham, CSS.
Bu darsda
- Browser Mode qanday ishlashini (Vitest, Vite, provayder, brauzer) tushuntira olasiz.
@vitest/browser-playwrightni o'rnatib, alohidabrauzerloyihasini sozlaysiz.- Lokator,
expect.elementvavitest/browserdagiuserEventbilan test yozasiz. - jsdom va haqiqiy brauzer farqini o'lchov bilan ko'rsatasiz va qaysi test qayerga borishini tanlaysiz.
vitest-browser-reactvatoMatchScreenshotnima ekanini bilasiz.
Oldin bilishingiz kerak: RTL II: foydalanuvchi harakatlari, RTL III: providerlar, MSW, dialog JS'dan.
1. Nega bu kerak?
React Testing Library I darsida bir topilma bo'lgan edi: jsdom 30.1.2 da <dialog> ning showModal() metodi yo'q. Kanon o'chirish dialogini testda open atributi bilan ochdi. Dialog ko'rindi — lekin bu haqiqiy modal emas edi. Fokus qayerga tushadi, Esc yopadimi, orqa fondagi tugma bosiladimi — hech biri tekshirilmadi.
Coverage hisobotida ham shu bo'shliq ko'ringan edi: buyruqlar oynasi (Ctrl+K) — 0 %. Uni jsdom'da ochib bo'lmaydi.
Kanon bir tajriba qildi. Bu darsdagi 6 ta brauzer testini olib, jsdom'da yurgizdi. Natija — 6 tadan 6 tasi yiqildi, ulardan 4 tasi shu xato bilan:
TypeError: ref.current?.showModal is not a functionTarjima: "showModal funksiya emas". Brauzerda bor metod jsdom'da yo'q. Demak, jsdom'dagi "yashil" test ba'zan yolg'on — u foydalanuvchi ko'radigan narsani tekshirmagan.
O'xshatish: haydovchilik maktabidagi trenajor. U arzon va tez — har kuni o'nlab mashq. Lekin unda yo'l chuqurlari, yomg'ir, haqiqiy tormoz yo'q. Imtihon esa haqiqiy yo'lda. jsdom — trenajor, Browser Mode — haqiqiy yo'l.
flowchart LR
T["test fayli"] --> J["jsdom (Node ichida)"]
T --> B["haqiqiy Chrome"]
J --> J1["tez: ~0.1 s/test"]
J --> J2["showModal yo'q, o'lcham 0"]
B --> B1["sekin: ~0.64 s/test"]
B --> B2["modal, fokus, CSS — haqiqiy"]Rasmga qarang: bitta test fayli ikki muhitda ishlashi mumkin. Chap yo'l tez, lekin brauzerning bir qismi yo'q. O'ng yo'l sekin, lekin hamma narsa haqiqiy.
2. Browser Mode qanday ishlaydi
2.1 To'rt ishtirokchi
Odatdagi Vitest testi Node ichida ishlaydi, document ni jsdom beradi (Vitest I). Browser Mode'da test kodi brauzer sahifasida bajariladi:
- Vitest (Node) — testlarni topadi, natijani terminalga chiqaradi.
- Vite server — test faylini va uning importlarini (React, komponentlar) brauzerga beradi — xuddi
npm run devdagidek. - Provayder (provider) — brauzerni ochadigan va boshqaradigan vosita. Bizda — Playwright. Xuddi televizor pulti: televizor (brauzer) o'zi ko'rsatadi, pult esa tugmalarni bosadi.
- Brauzer — Chrome. Test fayli shu yerda, oddiy sahifa kabi ishlaydi.
Provayder brauzerga buyruqni CDP (Chrome DevTools Protocol) orqali yuboradi. CDP — Chrome'ni tashqaridan boshqarish kanali: DevTools ham Chrome bilan aynan shu kanal orqali gaplashadi.
sequenceDiagram
participant V as Vitest (Node)
participant S as Vite server
participant P as Playwright
participant C as Chrome
V->>P: brauzerni och (headless)
P->>C: ishga tushirish
C->>S: test faylini so'raydi
S-->>C: test + React + komponent
C->>V: userEvent.click(...)
V->>P: bosish buyrug'i
P->>C: CDP: haqiqiy sichqoncha hodisasi
C-->>V: natija: ✓ yoki ×Diagrammadagi muhim qator — oxiridan ikkinchisi. Bosish JavaScript'dan "taqlid" qilinmaydi: Playwright Chrome'ga "shu nuqtada sichqoncha bosildi" deydi. Chrome uni xuddi odam bosgandek qabul qiladi.
2.2 Uch provayder
| Provayder | Paket | Hodisalar | Qachon |
|---|---|---|---|
playwright |
@vitest/browser-playwright |
CDP — haqiqiy | asosiy tanlov, CI ham |
webdriverio |
@vitest/browser-webdriverio |
WebDriver — haqiqiy | jamoa WebdriverIO'da bo'lsa |
preview |
@vitest/browser-preview |
@testing-library/user-event taqlidi |
faqat lokal ko'rib chiqish |
Vitest hujjatiga ko'ra preview headless rejimni qo'llamaydi (oyna doim ochiladi) va uning userEvent i oddiy @testing-library/user-event ning o'zi. Shuning uchun CI uchun yaramaydi. Kanon — playwright.
Tekshirib ko'ring: Browser Mode'da test kodi qayerda bajariladi — Node'dami yoki brauzerdami?
console.lognatijasi qayerga chiqadi?
Javob
Test kodi brauzer sahifasida bajariladi — shuning uchun document, showModal, CSS haqiqiy. console.log esa terminalda ko'rinadi: Vitest brauzer konsolini ushlab, Node'dagi hisobotga yuboradi (pastdagi chiqishlarda stdout | … qatorlari shundan).
3. O'rnatish va sozlash
3.1 Ikki paket
npm i -D -E @vitest/browser-playwright@5.0.4 playwright@1.64.0Kanondagi o'lchov: +16 paket, +22.1 MB node_modules, o'rnatish 16 s. @vitest/browser-playwright — Vitest bilan bir xil versiya bo'lishi shart (5.0.4). playwright — brauzerni boshqaradigan kutubxona; keyingi darsdagi e2e testlar ham shunga tayanadi.
Brauzerning o'zi-chi? Ikki yo'l bor:
- Playwright o'z brauzerini yuklaydi:
npx playwright install chromium. U Playwright'ning shu versiyasiga moslangan "Chrome for Testing" ni foydalanuvchi papkasidagims-playwrightkeshiga tushiradi. - Kompyuterdagi Chrome:
channel: "chrome". Hech narsa yuklanmaydi — o'rnatilgan Google Chrome ishlatiladi.
Kanon ikkinchisini tanladi: brauzer yuklanmadi. Bu disk va vaqtni tejaydi. Narxi: testlar Chrome versiyasiga bog'liq bo'lib qoladi — Chrome yangilansa, natija ham o'zgarishi mumkin.
3.2 vitest.config.ts — to'rtinchi loyiha
Vitest I da projects — bitta konfiguratsiyada bir nechta "loyiha" (har biri o'z muhiti bilan) — ni ko'rgan edik: mantiq (node) va komponent (jsdom). Kanon yana bittasini qo'shdi:
{
// 22/#08: Browser Mode — HAQIQIY Chrome (headless,
// Playwright orqali, tizimdagi Chrome: channel "chrome" —
// brauzer yuklanmaydi). jsdom'da yo'q narsalar uchun:
// <dialog>.showModal, fokus, :modal, lazy() chunk.
// Sekinroq — shuning uchun alohida: npm run test:brauzer
extends: true,
// MSW service worker (mockServiceWorker.js) shu
// papkadan "/" ga
publicDir: "tekshiruv/msw/ommaviy",
test: {
name: "brauzer",
include: ["tekshiruv/**/*.brauzer.tsx"],
setupFiles: ["tekshiruv/brauzer-sozlash.ts"],
browser: {
enabled: true,
headless: true,
provider: playwright({
launchOptions: { channel: "chrome" },
}),
instances: [{ browser: "chromium" }],
},
},
},Fayl boshida import: import { playwright } from "@vitest/browser-playwright";.
| Kalit | Nima qiladi |
|---|---|
include |
faqat *.brauzer.tsx — jsdom testlari bilan aralashmaydi |
browser.enabled |
shu loyiha brauzerda ishlaydi |
headless: true |
oynasiz — ekranda hech narsa ochilmaydi |
provider |
Playwright; channel: "chrome" — tizimdagi Chrome |
instances |
qaysi brauzer(lar): chromium, firefox, webkit |
publicDir |
MSW fayli shu papkadan beriladi (pastda) |
Headless (oynasiz) — brauzer ishlaydi, lekin ekranda oyna chizmaydi. CI serverida ekran yo'q, shuning uchun u yerda doim headless.
instances ga uchta brauzer yozilsa, har test uch marta — har brauzerda bir marta ishlaydi. Kanon faqat chromium bilan qoldi.
3.3 Nega npm test ga kirmadi
package.json ga alohida skript: "test:brauzer": "vitest run --project brauzer". Asosiy npm test esa avvalgidek faqat mantiq va komponent. Sabab ikkita: brauzer testlari sekinroq va ular uchun kompyuterda Chrome bo'lishi kerak. Har saqlashda ishlaydigan tez testlar tez qolsin.
4. Birinchi brauzer testi
Kanondagi dialoglar.brauzer.tsx ning birinchi testi. Avval yordamchi — dialogni chizib, haqiqiy showModal() bilan ochadi:
function dialogniOch(onJavob = vi.fn()) {
const ref = createRef<HTMLDialogElement>();
render(
<>
<button type="button">Orqadagi tugma</button>
<OchirishDialog ref={ref} matn="Non olish" onJavob={onJavob} />
</>,
);
ref.current?.showModal();
return { dialog: ref.current, onJavob };
}render — o'sha RTL dan: React Testing Library brauzerda ham ishlaydi. Keyin test:
test("modal: nomi, tavsifi, fokus «Bekor qilish»da", async () => {
const { dialog } = dialogniOch();
const oyna = page.getByRole("dialog", {
name: "Vazifa o'chirilsinmi?",
});
await expect.element(oyna).toBeVisible();
await expect
.element(oyna)
.toHaveAccessibleDescription(
"«Non olish» ro'yxatdan o'chiriladi.",
);
expect(dialog?.matches(":modal")).toBe(true);
await expect
.element(page.getByRole("button", { name: "Bekor qilish" }))
.toHaveFocus();
});Uch yangi narsa bor: page va expect.element (keyingi bo'lim) hamda :modal. :modal — CSS psevdo-klassi: element hozir modal holatdami (orqa fon bloklanganmi). jsdom buni bilmaydi, Chrome biladi. Oxirgi qator — showModal fokusni birinchi tugmaga qo'yganini tekshiradi: Malika uchun bu "dialog ochildi, men qayerdaman?" savolining javobi.
Biz kanon testlarini terminalda yurgizdik (Vitest 5.0.4, Chrome headless):
npx vitest run --project brauzer --reporter=verboseQatorlar uzun — har birining boshiga qarang: ✓, keyin |brauzer (chromium)|, oxirida vaqt.
RUN v5.0.4 C:/Users/…/vazifalar
✓ |brauzer (chromium)| tekshiruv/dialoglar.brauzer.tsx > OchirishDialog — native <dialog> (showModal) > modal: nomi, tavsifi, fokus «Bekor qilish»da 37ms
✓ |brauzer (chromium)| tekshiruv/dialoglar.brauzer.tsx > OchirishDialog — native <dialog> (showModal) > Esc — yopiladi, javob false 17ms
✓ |brauzer (chromium)| tekshiruv/dialoglar.brauzer.tsx > OchirishDialog — native <dialog> (showModal) > «O'chirish» — form method=dialog, returnValue → javob true 105ms
✓ |brauzer (chromium)| tekshiruv/dialoglar.brauzer.tsx > OchirishDialog — native <dialog> (showModal) > orqa fon inert: Tab dialogdan tashqaridagi tugmaga o'tmaydi 31ms
✓ |brauzer (chromium)| tekshiruv/dialoglar.brauzer.tsx > BuyruqlarOynasi — Ctrl+K, lazy chunk, fokus qaytishi > Ctrl+K — oyna, qidiruv maydoni fokusda; Esc — fokus qaytadi 499ms
✓ |brauzer (chromium)| tekshiruv/dialoglar.brauzer.tsx > App — o'chirish oqimi (MSW service worker bilan) > «O'chirish» → dialog → tasdiq: qator yo'q, DELETE /1 ketdi 311ms
Test Files 1 passed (1)
Tests 6 passed (6)
Duration 3.66s (tests 53%, import 18%, setup 18%, worker 11%)Yangi belgi — |brauzer (chromium)|: loyiha nomi va brauzer. Qolgani tanish. Sizda vaqtlar boshqacha chiqadi.
5. Lokatorlar va expect.element
5.1 Lokator — element emas, "qidiruv yo'riqnomasi"
RTL'dagi screen.getByRole(...) elementni hozir qidiradi va DOM elementini qaytaradi. Topmasa — darhol xato.
Lokator (locator) boshqacha. page.getByRole("button", { name: "Saqlash" }) hech narsa qidirmaydi — u "qanday topish kerak" degan yo'riqnomani saqlaydi. Qidiruv keyin bo'ladi: bosganda (.click()) yoki tekshirganda (expect.element). O'xshatish: taksiga "Chorsu bozori, asosiy darvoza" deysiz — bu manzil, hali yetib borganingiz yo'q. Haydovchi manzilni kerak paytda topadi.
RTL screen.getByRole |
Vitest page.getByRole |
|
|---|---|---|
| Qaytaradi | DOM element | lokator |
| Qachon qidiradi | darhol | harakat yoki tekshiruv paytida |
| Topilmasa | darhol xato | qayta urinadi |
| Kutish | findBy… alohida |
o'zi kutadi |
| Bosish | user.click(element) |
await lokator.click() |
Lokatorlar oilasi RTL so'rovlariga juda o'xshaydi: getByRole, getByLabelText, getByPlaceholder, getByText, getByAltText, getByTitle, getByTestId. So'rovlar ustuvorligi ham o'sha: avval rol.
5.2 expect.element — qayta urinadigan tekshiruv
await expect.element(lokator).toBeVisible() — "element paydo bo'lib, ko'rinmaguncha qayta tekshir". Bu RTL'dagi await waitFor(() => expect(...)) ning qisqa shakli: waitFor yozish shart emas. Matcherlar — o'sha jest-dom: toBeVisible, toHaveFocus, toHaveAccessibleName va boshqalar.
await ni unutmang. expect.element — Promise: await siz test tekshiruvni kutmasdan tugaydi.
Element umuman topilmasa nima bo'ladi? Biz ataylab yo'q nomli tugmani qidirdik — sahifada faqat «Qo'shish» bor:
test("yo'q tugma", async () => {
render(<button>Qo'shish</button>);
await expect
.element(page.getByRole("button", { name: "Saqlash" }))
.toBeVisible();
}); ❯ |brauzer (chromium)| tekshiruv/tajriba2.brauzer.tsx (1 test | 1 failed) 15100ms
× yo'q tugma 15093ms
FAIL |brauzer (chromium)| tekshiruv/tajriba2.brauzer.tsx > yo'q tugma
VitestBrowserElementError: Cannot find element with locator: getByRole('button', { name: 'Saqlash' })
ARIA tree:
- button "Qo'shish"
Failure screenshot:
- .vitest/attachments/failure-screenshots/tajriba2.brauzer.tsx/yo-q-tugma.pngTarjima: "Lokator bo'yicha element topilmadi". Uch foydali narsa bor:
ARIA tree— sahifadagi rollar va nomlar daraxti. Darhol ko'rinadi: tugma bor, nomi boshqa.Failure screenshot— yiqilgan paytdagi sahifa rasmi..vitest/papkasiga tushadi.- 15 093 ms — test 15 soniya kutdi. Browser Mode'da test vaqti chegarasi sukut bo'yicha 15 s (Node'da — 5 s). Nomdagi bitta harf xatosi testni 15 soniyaga "osib" qo'yadi.
5.3 Yashirin elementlar
Lokator RTL kabi yashirin elementni o'tkazib yuboradi: display: none, yopiq <dialog>, hidden. Bu to'g'ri — Malika uchun ular yo'q (RTL I dagi hidden sozlamasi). Lekin bir tuzoq bor. Biz mashq yechimini yozayotganda shunday test yozdik:
await expect
.element(page.getByRole("dialog", { name: "Bron" }))
.not.toBeVisible();Dialog yopildi — va test 15 soniya kutib yiqildi: Cannot find element with locator. Yopiq dialog yashirin, lokator uni topmaydi, not.toBeVisible esa "avval top, keyin tekshir" deb kutaveradi. Ikki to'g'ri yo'l:
getByRole("dialog", { name: "Bron", includeHidden: true })— yashirinni ham top, keyinnot.toBeVisible()..not.toBeInTheDocument()— bu matcher lokatorni kutmaydi, "topilmadi" ni o'zi muvaffaqiyat deb oladi. Kanonning "Esc — yopiladi" testi shunday yozilgan.
Ikkinchisining nozikligi: yopiq dialog DOM'da turibdi, lekin test "hujjatda yo'q" deydi. Lokator uchun "hujjatda" — "foydalanuvchi ko'radigan daraxtda" degani.
Tekshirib ko'ring: Nega
page.getByRole(...)ni test boshida o'zgaruvchiga saqlab, element hali chizilmagan bo'lsa ham keyin ishlatish mumkin,screen.getByRole(...)ni esa yo'q?
Javob
Lokator hech narsani darhol qidirmaydi — u faqat "qanday topish kerak" ni eslab qoladi. Har click() yoki expect.element da qidiruv yangidan bo'ladi. Shuning uchun element keyin paydo bo'lsa yoki qayta chizilsa ham topiladi. screen.getByRole esa shu zahoti qidiradi. Element yo'q bo'lsa — xato, bor bo'lsa — aynan o'sha DOM tugunini qaytaradi. React uni almashtirsa, eski tugun qo'lingizda qoladi.
6. userEvent: haqiqiy hodisalar
RTL II dagi @testing-library/user-event foydalanuvchini JavaScript bilan taqlid qiladi: pointerdown, mousedown, focus, click hodisalarini ketma-ket o'zi yaratib yuboradi. Bu juda yaxshi taqlid, lekin baribir taqlid.
Brauzerda har hodisaning isTrusted belgisi bor — "bu hodisani foydalanuvchining haqiqiy harakati yaratdimi?" JavaScript yaratgan hodisada u false. «Ishga tushir» ni bosing — natija oynasi haqiqiy Chrome, lekin hodisani user-event yaratadi:
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { expect, test } from "vitest";
function OrderButton({ onPress }) {
return (
<button onClick={(e) => onPress(e.isTrusted)}>
Buyurtma berish
</button>
);
}
test("user-event — hodisa JavaScript'dan", async () => {
const seen = [];
render(<OrderButton onPress={(t) => seen.push(t)} />);
const user = userEvent.setup();
await user.click(screen.getByRole("button"));
console.log(seen);
expect(seen).toEqual([false]);
});Konsolda:
(1) [false]
✓ user-event — hodisa JavaScript'dan
Testlar: 1 o'tdi (jami 1)Xuddi shu komponentni Browser Mode'da ikki xil bosib ko'rdik — vitest/browser dagi userEvent va user-event kutubxonasi bilan. Terminal:
stdout | tekshiruv/tajriba.brauzer.tsx > isTrusted: vitest/browser userEvent
vitest/browser: [
true,
]
stdout | tekshiruv/tajriba.brauzer.tsx > isTrusted: @testing-library/user-event
user-event: [
false,
]vitest/browser ning userEvent i Playwright orqali Chrome'ga haqiqiy sichqoncha hodisasini yuboradi — true. Bu nega muhim? Brauzerning ba'zi imkoniyatlari faqat haqiqiy harakatdan keyin ochiladi: to'liq ekran (requestFullscreen), yangi oyna, buferga yozish. Taqlid hodisa ularni ochmaydi. Bundan tashqari, haqiqiy bosish element ustida boshqa narsa (masalan, toast) turgan bo'lsa, o'sha narsaga tegadi — xuddi foydalanuvchidagidek.
API deyarli bir xil — RTL II dagi yozuvlar ishlaydi:
import { page, userEvent } from "vitest/browser";
await userEvent.keyboard("{Escape}");
await userEvent.keyboard("{Control>}k{/Control}"); // Ctrl+K
await page.getByRole("button", { name: "O'chirish" }).click();
await page.getByLabelText("Matn").fill("Non olish");setup() shart emas. Lokatorning o'z metodlari ham bor: click(), fill(), hover(), selectOptions().
7. jsdom va haqiqiy brauzer: nima farq qiladi
7.1 O'lchov va joylashuv
jsdom sahifani chizmaydi — joylashuvni (layout) hisoblamaydi. Shuning uchun hamma o'lchamlar 0. Natija oynasi esa haqiqiy Chrome. Bu blokni natija oynasida ishga tushiring:
import { render, screen } from "@testing-library/react";
import { expect, test } from "vitest";
test("tugma kengligi — 160 px", () => {
render(<button style={{ width: 160 }}>Buyurtma berish</button>);
const button = screen.getByRole("button");
const width = button.getBoundingClientRect().width;
console.log(width);
expect(width).toBe(160);
});Chrome'da (natija oynasi) konsol: 160, ✓ tugma kengligi — 160 px, Testlar: 1 o'tdi (jami 1). Xuddi shu testni Vitest + jsdom'da yurgizdik:
FAIL |komponent| tekshiruv/tajriba-layout.test.tsx > layout: kenglik
AssertionError: expected +0 to be 160 // Object.is equality
- Expected
+ Received
- 160
+ 0jsdom'da kenglik — 0. Tooltip qayerda chiqishi, matn qatorga sig'ishi, sticky sarlavha — bularning hammasi o'lchamga tayanadi va jsdom'da tekshirilmaydi.
7.2 Farqlar jadvali
| Narsa | jsdom 30.1.2 | Chrome (Browser Mode) |
|---|---|---|
dialog.showModal() |
yo'q (TypeError) | bor, :modal, Esc |
O'lcham (getBoundingClientRect) |
doim 0 | haqiqiy |
| Oyna kengligi | doim 1024, o'zgarmaydi | page.viewport(375, 700) |
matchMedia |
yo'q (kanon soxtasini qo'ygan) | haqiqiy media query |
isTrusted hodisa |
faqat taqlid | haqiqiy (CDP) |
| Service Worker | yo'q | bor (MSW setupWorker) |
| Bitta test (Duration / test soni, holat 08) | ~0.1 s | ~0.64 s |
Oyna kengligi va matchMedia ni biz tekshirdik: jsdom'da window.innerWidth — 1024. Browser Mode'da page.viewport(375, 700) dan keyin innerWidth — 375, media query ishlaydi (3-mashq).
7.3 Narx: vaqt
Kanon o'lchovi (dialoglar.brauzer.tsx, 6 test, i5-12500H) — grafikda devor soati vaqti (testdan boshlab terminal qaytguncha):
- Sovuq (1-marta)Vite deps + Chrome ishga tushishi24,3 s
- Issiq 16,64 s
- Issiq 28 s
- Issiq 36,94 s
Manba: O'lchandi: vazifalar kanoni (holat 08), i5-12500H, Windows 11, Node 24.21.0, Vitest 5.0.4, Playwright 1.64.0, Chrome headless, 2026-10-20
Birinchi ishga tushirish 24 soniya — Vite bog'liqliklarni tayyorlaydi va Chrome ochiladi. Keyin 7–8 s. Taqqoslash uchun: 82 ta jsdom komponent testi taxminan shuncha vaqt oladi.
Bitta testga qancha tushadi? Bu qismda hamma joyda bir usul: Vitest hisobotidagi Duration ni testlar soniga bo'lamiz. Holat 08 ning yakuniy yurgizishida Duration 3.84s, 6 test — ≈ 0.64 s bitta brauzer testiga. jsdom komponent loyihasi: 82 test, Duration 7–10 s — ≈ 0.1 s. Brauzer ishga tushishi ham shu vaqt ichida, shuning uchun testlar ko'paygan sari bittasiga kamroq tushadi (keyingi holatlarda raqam boshqacha chiqishi tabiiy).
Qoida shundan kelib chiqadi: Browser Mode'ga faqat jsdom ko'ra olmaydigan narsa o'tadi. Modal, fokus tuzog'i, o'lcham, CSS, haqiqiy hodisa. Oddiy "tugma bosilsa matn chiqadi" — jsdom'da qoladi.
Tekshirib ko'ring: Uch test bor: (a) narxni formatlash funksiyasi; (b) filtr tugmasi bosilsa ro'yxat kamayadi; (c) mobil ekranda (375 px) menyu tugmasi ko'rinadi. Har birini qaysi loyihaga (
mantiq,komponent,brauzer) qo'yasiz?
Javob
(a) mantiq — DOM kerak emas, node muhiti eng tez. (b) komponent — DOM kerak, lekin o'lcham yoki modal kerak emas: jsdom yetadi. (c) brauzer — media query va ekran kengligi faqat haqiqiy brauzerda ishlaydi; jsdom'da oyna doim 1024 px.
8. vitest-browser-react va MSW brauzerda
8.1 vitest-browser-react
Vitest hamjamiyati (vitest-community) React uchun alohida kichik paket chiqaradi — vitest-browser-react. Uning render i lokatorlar bilan ishlaydi:
import { render } from "vitest-browser-react";
test("buyurtma tugmasi", async () => {
const screen = await render(<OrderButton onPress={() => {}} />);
await screen.getByRole("button").click();
const done = screen.getByText("Qabul qilindi");
await expect.element(done).toBeVisible();
});Farqlari: render — await bilan (Promise qaytaradi), qaytgan screen dagi so'rovlar — lokatorlar, tozalash testdan oldin bo'ladi. Shuning uchun yiqilgan testning komponenti brauzerda ko'rinib turadi. Biz sinab ko'rdik: vitest-browser-react 2.3.0 Vitest 5.0.4 bilan ishladi, hajmi 36 KB.
Kanon uni qo'shmadi. Sabab: RTL'ning render i brauzerda ham ishlaydi, providerli chiz() yordamchisi (RTL III) o'zgarishsiz qoldi. Bitta paket kam, bitta usul kam. Yangi loyihada, Browser Mode'dan boshlasangiz, vitest-browser-react — tabiiy tanlov.
8.2 MSW: endi haqiqiy service worker
MSW darsida Node'da setupServer ishlatgan edik — u fetch ni ichkaridan o'raydi. Brauzerda MSW boshqacha ushlaydi: haqiqiy service worker orqali (Service Worker). Handlerlar o'sha — faqat ulash boshqa:
import { setupWorker } from "msw/browser";
import { handlerlar } from "./soxta-api.ts";
export const worker = setupWorker(...handlerlar);Sozlash faylida (brauzer-sozlash.ts):
// Top-level await: SW ro'yxatdan o'tib, sahifani boshqarguncha
// kutadi — aks holda birinchi so'rovlar ushlanmay tarmoqqa ketadi.
// Faqat API so'rovlari xato: Vite modul so'rovlari ham SW orqali
// o'tadi
await worker.start({
quiet: true,
onUnhandledFrame: "bypass",
});Ikki nozik joy:
mockServiceWorker.js(MSW'ning worker fayli,npx msw inityaratadi) ilovaningpublic/iga emas,tekshiruv/msw/ommaviy/ga qo'yildi.public/da ilovaning o'zsw.jsi bor — ikki worker bir-biriga xalaqit bermasin. Shuning uchun konfiguratsiyadapublicDir.onUnhandledFrame: "bypass"— handleri yo'q so'rov tarmoqqa o'tadi. Node'da kanon"error"qo'ygan edi. Brauzerda esa Vite'ning modul so'rovlari ham worker orqali o'tadi —"error"ularni ham yiqitardi.
9. Vizual tekshiruv: toMatchScreenshot (qisqa)
Browser Mode haqiqiy chizadi — demak, rasmga olish mumkin. toMatchScreenshot elementni rasmga oladi va oldingi "oltin" rasm bilan solishtiradi. Biz sinab ko'rdik:
test("tugma ko'rinishi", async () => {
render(<button style={{ padding: 8 }}>Buyurtma berish</button>);
await expect
.element(page.getByRole("button"))
.toMatchScreenshot("buyurtma-tugmasi");
});Birinchi ishga tushirish:
Error: expect(element).toMatchScreenshot()
No existing reference screenshot found; a new one was created. Review it before running tests again.
Reference screenshot:
C:/Users/…/tekshiruv/__screenshots__/tajriba3.brauzer.tsx/buyurtma-tugmasi-chromium-win32.pngTarjima: "Oltin rasm topilmadi; yangisi yaratildi. Qayta yurgizishdan oldin uni ko'rib chiqing." Birinchi marta test ataylab yiqiladi — siz rasmni ko'rib, to'g'riligini tasdiqlashingiz kerak. Ikkinchi ishga tushirishda — ✓.
Fayl nomiga qarang: -chromium-win32. Rasm brauzerga va operatsion tizimga bog'langan: Linux'dagi CI boshqa shrift bilan boshqa rasm chizadi. Bu muammo va uning yechimlari — Visual regression testlar darsida.
10. Vazifalar qadami: Browser Mode (holat 08)
10.1 Branch va o'rnatish
git switch -c test/brauzer-rejimi
npm i -D -E @vitest/browser-playwright@5.0.4 playwright@1.64.0Kanon: +16 paket, +22.1 MB, 16 s. Brauzer yuklanmadi (channel: "chrome"). vitest.config.ts ga «vitest.config.ts — to'rtinchi loyiha» bo'limidagi blok, package.json ga test:brauzer skripti.
10.2 Testlar — tekshiruv/dialoglar.brauzer.tsx (6)
| Guruh | Testlar |
|---|---|
OchirishDialog |
modal: nom, tavsif, fokus; Esc → false; «O'chirish» → true; Tab orqa fonga chiqmaydi |
BuyruqlarOynasi |
Ctrl+K → oyna, qidiruv maydoni fokusda; Esc → fokus joyiga qaytadi |
App |
o'chirish oqimi: dialog → tasdiq → qator yo'q, DELETE /1 (MSW worker) |
Buyruqlar oynasi testi — jsdom'da umuman yozib bo'lmaydigan oqim:
await boshqa.click();
await userEvent.keyboard("{Control>}k{/Control}");
await expect
.element(page.getByRole("dialog", { name: "Buyruqlar" }))
.toBeVisible();
// Birinchi ochilishda BuyruqlarRoyxati chunk'i (cmdk)
// yuklanadi — maydon kelgach fokusni o'zi oladi (20/#11)
await expect.element(page.getByRole("combobox")).toHaveFocus();
await userEvent.keyboard("{Escape}");lazy() bilan ajratilgan qism (code splitting) haqiqatan tarmoqdan yuklanadi, expect.element uni kutadi. Bobur (faqat klaviatura) uchun eng muhim narsa — oxirgi tekshiruv: yopilgach fokus avvalgi maydonga qaytadi.
Kanon bir topilmani izohga yozdi: orqa fon inert testida Vitest lokatori includeHidden: true bilan orqadagi tugmani topadi — shuning uchun tekshiruv "topildimi" emas, "fokus unga o'tmadimi" orqali (Tab uch marta, har safar not.toHaveFocus()).
10.3 Uch tur tuzog'i (o'lchandi)
npm run tip (tsc -b) birinchi o'tishda yiqildi — uch sabab bilan:
- TS2320 × 16.
@vitest/browserjest-dom matcherlarining o'z tur nusxasini olib keladi. RTL I dagi o'zimizningdeclare module "vitest"e'lonimiz u bilan to'qnashdi. Yechim —jest-dom.tsbirinchi qatorida bitta manba:/// <reference types="@vitest/browser/matchers" />. Vitest nusxasida hali yo'q ikki matcher (toBePressed,toBePartiallyPressed) qo'lda qo'shildi. - 190+ TS2304. Playwright turlari DOM'ga (
HTMLElement,Node) tayanadi.vitest.config.tsesatsconfig.node.jsonda edi — u yerda DOM ataylab yo'q (tsconfig app/node). Yechim —vitest.config.tsuchun alohidatsconfig.vitest.json(DOM bilan). BufferEncodingtopilmadi.vitest/browserturlari Node'ningBufferEncodingturini so'raydi. TS 6 datypessukut bo'yicha bo'sh —@types/nodeo'zi ulanmaydi. Yechim —tsconfig.test.jsonda"types": ["node"].
{
// 22/#08: vitest.config.ts alohida — u
// @vitest/browser-playwright ni import qiladi, Playwright turlari
// esa DOM'ga (HTMLElement, Node) tayanadi. tsconfig.node.json da
// DOM ataylab yo'q (16/#42) — uni buzmaslik uchun faqat shu fayl
// DOM lib bilan tekshiriladi
"extends": "./tsconfig.node.json",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo",
"lib": ["es2024", "DOM"]
},
"include": ["vitest.config.ts"]
}.vitest/ (yiqilgan test rasmlari) — .gitignore, ESLint va Prettier e'tiboridan chiqarildi; knip'ga MSW worker fayli ignore ga.
10.4 Natija va commit
- Testlar: Vitest 348 (o'zgarmadi) + brauzer 6.
npm run test:brauzer— Duration 3.84 s; sovuq start 24.3 s. - Coverage: satrlar 62.72 % — o'zgarmadi.
test:qamrovfaqatmantiqvakomponentni o'lchaydi; brauzer testlari raqamga kirmaydi (Browser Mode'da coverage yig'ish mumkin, kanonda o'lchanmagan). - Ilova JS — 206.7 KB gzip, o'zgarmadi: test paketlari faqat
devDependenciesda. - Lint,
format:check,tip, knip — toza. Zavod e2e: 159, 72, 100, 77, 81; axe — 0.
git add -A
git commitCommit (kanon): test: Vitest Browser Mode (Playwright, tizim Chrome) — dialog, Ctrl+K, o'chirish oqimi.
11. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
test 15 s osilib, Cannot find element with locator |
nom xato yoki element yashirin | ARIA tree ga qarang; yashirin — includeHidden: true |
not.toBeVisible() 15 s kutadi |
yashirin elementni lokator topmaydi | includeHidden: true yoki not.toBeInTheDocument() |
| tekshiruv "o'tdi", lekin hech narsa tekshirilmadi | expect.element oldida await yo'q |
doim await expect.element(...) |
Executable doesn't exist … npx playwright install |
Playwright brauzeri yo'q | npx playwright install chromium yoki channel: "chrome" |
TS2320 (jest-dom turlari) |
ikki tur nusxasi | /// <reference types="@vitest/browser/matchers" /> |
| MSW so'rovni ushlamaydi | worker ishga tushguncha so'rov ketgan | await worker.start() sozlash faylida |
| hamma test sekinlashdi | jsdom testlari ham brauzerga ko'chgan | faqat jsdom ko'rmaydiganlar — *.brauzer.tsx |
12. Mashqlar
Mashqlar kurs/mashqlar/22/08-brauzer-rejimi/ da. Brauzer testlari natija oynasida ishlamaydi — ularni vazifalar loyihangizda (holat 08 dan) tekshiruv/ ga qo'yib, npx vitest run --project brauzer bilan yurgizing.
1-mashq (oson): bo'sh joylarni to'ldiring
- Brauzer loyihasini yoqadigan kalit:
browser: { : true } - Qayta urinadigan tekshiruv:
await expect.(lokator).toBeVisible() - Haqiqiy hodisalar uchun import:
import { page, userEvent } from "" - Oynasiz rejim:
: true - Ekran kengligini o'zgartirish:
await page.(375, 700)
Yechim
- enabled — shu loyiha brauzerda ishlaydi.
- element —
expect.elementlokatorni shart bajarilguncha qayta tekshiradi. - vitest/browser —
page(lokatorlar) vauserEvent(CDP orqali haqiqiy hodisalar) shu yerda. - headless — CI'da ekran yo'q, oyna chizilmaydi.
- viewport — media query'larni sinash uchun; jsdom'da kenglik doim 1024.
2-mashq (o'rta): fokus qaytadimi?
«Bahor» saytida «Stol bron qilish» tugmasi <dialog> ni showModal() bilan ochadi. Dialog ichida «Mehmonlar soni» maydoni va <form method="dialog"> ichida «Yopish» tugmasi. Ikki brauzer testi yozing: (1) ochilganda fokus maydonda; Esc — dialog yopiladi va fokus «Stol bron qilish» tugmasiga qaytadi; (2) «Yopish» bosilsa ham fokus tugmaga qaytadi.
Ishora: yopiq dialogni tekshirish — «Yashirin elementlar» bo'limidagi tuzoqni eslang. type="number" ning roli — spinbutton (RTL I).
Yechim
import { useRef } from "react";
import { expect, test } from "vitest";
import { page, userEvent } from "vitest/browser";
import { render } from "@testing-library/react";
function BookingDialog() {
const ref = useRef<HTMLDialogElement>(null);
return (
<>
<button type="button" onClick={() => ref.current?.showModal()}>
Stol bron qilish
</button>
<dialog ref={ref} aria-labelledby="bron-sarlavha">
<h2 id="bron-sarlavha">Bron</h2>
<label>
Mehmonlar soni
<input type="number" defaultValue={2} />
</label>
<form method="dialog">
<button>Yopish</button>
</form>
</dialog>
</>
);
}
// Yopiq dialog a11y daraxtida yo'q — lokator uni topishi uchun
// includeHidden: true (aks holda not.toBeVisible 15 s kutib yiqiladi)
const dialog = () =>
page.getByRole("dialog", { name: "Bron", includeHidden: true });
test("Esc — dialog yopiladi, fokus tugmaga qaytadi", async () => {
render(<BookingDialog />);
const open = page.getByRole("button", { name: "Stol bron qilish" });
await open.click();
await expect.element(dialog()).toBeVisible();
// showModal birinchi fokuslanadigan elementga fokus beradi
await expect
.element(page.getByRole("spinbutton", { name: "Mehmonlar soni" }))
.toHaveFocus();
await userEvent.keyboard("{Escape}");
await expect.element(dialog()).not.toBeVisible();
await expect.element(open).toHaveFocus();
});
test("«Yopish» — fokus yana tugmada", async () => {
render(<BookingDialog />);
const open = page.getByRole("button", { name: "Stol bron qilish" });
await open.click();
await page.getByRole("button", { name: "Yopish" }).click();
await expect.element(dialog()).not.toBeVisible();
await expect.element(open).toHaveFocus();
});Biz yurgizdik — ikkalasi o'tdi (133 ms va 147 ms). Fokusni ochgan tugmaga qaytarishni brauzer o'zi qiladi — kodda hech narsa yozilmagan. jsdom'da bu testni yozib ham bo'lmasdi: showModal yo'q.
3-mashq (qiyin): mobil menyu
«Bahor» sarlavhasida «Menyu» tugmasi va «Taomlar», «Bron» havolalari bor. CSS: 600 px dan tor ekranda tugma ko'rinadi, havolalar yashirinadi; kengda — aksincha. Ikki test yozing: 375 px va 1280 px. Har testdan keyin kenglikni 1280 ga qaytaring.
Ishora: CSS'ni <style> matni bilan komponent ichida bering; page.viewport(kenglik, balandlik) Promise qaytaradi; yashirin element — includeHidden: true.
Yechim
import { afterEach, expect, test } from "vitest";
import { page } from "vitest/browser";
import { render } from "@testing-library/react";
const css = `
.burger { display: none; }
@media (max-width: 600px) {
.burger { display: inline-block; }
.links { display: none; }
}
`;
function Header() {
return (
<header>
<style>{css}</style>
<button className="burger">Menyu</button>
<nav className="links" aria-label="Asosiy">
<a href="#menyu">Taomlar</a> <a href="#bron">Bron</a>
</nav>
</header>
);
}
const burger = () =>
page.getByRole("button", { name: "Menyu", includeHidden: true });
const link = () =>
page.getByRole("link", { name: "Taomlar", includeHidden: true });
afterEach(async () => {
await page.viewport(1280, 720);
});
test("telefon (375 px): faqat «Menyu»", async () => {
await page.viewport(375, 700);
render(<Header />);
await expect.element(burger()).toBeVisible();
await expect.element(link()).not.toBeVisible();
console.log("kenglik:", window.innerWidth);
});
test("kompyuter (1280 px): havolalar, «Menyu» yo'q", async () => {
await page.viewport(1280, 720);
render(<Header />);
await expect.element(link()).toBeVisible();
await expect.element(burger()).not.toBeVisible();
console.log("kenglik:", window.innerWidth);
});Terminalda (biz yurgizdik):
stdout | tekshiruv/mashq22-3-mobil-menyu.brauzer.tsx > telefon (375 px): faqat «Menyu»
kenglik: 375
stdout | tekshiruv/mashq22-3-mobil-menyu.brauzer.tsx > kompyuter (1280 px): havolalar, «Menyu» yo'q
kenglik: 1280afterEach muhim: viewport butun brauzer sahifasiniki. Qaytarilmasa, keyingi test faylidagi testlar ham 375 px da ishlab, tasodifan yiqilishi mumkin. jsdom'da esa innerWidth doim 1024 — media query hech qachon ishlamaydi.
4-mashq: Vazifalar qadami
test/brauzer-rejimi branch'ida «Vazifalar qadami» dagi o'zgarishlarni qiling. Avval jest-dom.ts ni o'zgartirmasdan npm run tip ni yurgizib, TS2320 xatolarini o'zingiz ko'ring.
Yechim
Tekshiruv ro'yxati:
npm run test:brauzer— 6/6; birinchi marta sekin (20 s atrofida), keyin 7–8 s.npm test— 348 (brauzer testlari bunga kirmaydi).dialoglar.brauzer.tsxni nusxalab.test.tsxqilib jsdom'da yurgizing:showModal is not a function— jsdom chegarasini o'z ko'zingiz bilan ko'rasiz. Nusxani o'chiring.- Bitta lokator nomini ataylab buzing: test 15 s kutadi,
.vitest/attachments/da rasm paydo bo'ladi. Qaytaring. npm run tip,lint,knip— toza.
13. Real ishda
- Dizayn tizimlari va UI kutubxonalari (Radix, React Aria kabi) fokus, klaviatura va joylashuvni haqiqiy brauzerda sinaydi — jsdom bunday komponentlar uchun yetmaydi.
- Odatdagi loyihada ko'pchilik komponent testi jsdom'da qoladi, Browser Mode — bir necha o'nta "og'ir" test uchun. Storybook testlari ham Browser Mode ustida ishlaydi — u yerda yana ko'ramiz.
- CI'da Browser Mode uchun brauzer kerak: GitHub'ning Ubuntu runner'ida Chrome bor, yo'q bo'lsa
npx playwright install --with-deps chromium(Playwright II). - Intervyu savollari: "jsdom nimani qila olmaydi?", "lokator va element farqi?", "nega hamma testni brauzerda yurgizmaymiz?".
Xulosa
- Browser Mode test faylini haqiqiy brauzerda yurgizadi; brauzerni provayder (Playwright) CDP orqali boshqaradi.
- Sozlash — alohida loyiha:
browser.enabled,provider: playwright(),instances; kanonda tizim Chrome'i (channel: "chrome"). page.getByRole— lokator: qidiruv harakat paytida;await expect.element(...)o'zi qayta uradi. Yashirin elementgaincludeHidden.vitest/browserdagiuserEvent— haqiqiy hodisa (isTrusted: true), user-event — taqlid.- jsdom'da o'lcham 0,
showModalyo'q, kenglik 1024 — bularni faqat brauzer tekshiradi; narxi ~6 baravar sekinlik. vazifalar: 348 + 6 brauzer testi, +22.1 MB, ilova 206.7 KB — o'zgarmadi.
Keyingi dars: Playwright I: e2e testlar asoslari — endi bitta komponent emas, butun ilova: production build, router, tarmoq va foydalanuvchi yo'li boshidan oxirigacha.
Manbalar
- Vitest: Browser Mode — "Why Browser Mode", "Configuring Playwright", "Locators", "Assertions" (
expect.element), "Interactivity API" (vitest.dev/guide/browser) - Vitest:
testTimeout— Node'da 5 000, Browser Mode'da 15 000 ms (vitest.dev/config/testtimeout) - Vitest: Preview provider cheklovlari (vitest.dev/config/browser/preview)
- vitest-browser-react — github.com/vitest-community/vitest-browser-react
- MSW: Browser integration —
setupWorker,npx msw init(mswjs.io/docs/integrations/browser) - MDN:
Event.isTrusted,HTMLDialogElement.showModal(),:modal - Haqiqiy chiqishlar: Vitest 5.0.4, @vitest/browser-playwright 5.0.4, Playwright 1.64.0, jsdom 30.1.2, Node 24.21.0, Chrome headless (2026-10-20)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!