IlmHamroh
JavaScript Full-stack/22-qism. Frontend testlash9/16-dars22 daqiqa
Mundarija (33)

Playwright I: e2e testlar asoslari — lokatorlar, auto-wait, web-first assertion va webServer

Qisqacha: Playwright — butun ilovani haqiqiy brauzerda (Chromium, Firefox, WebKit) foydalanuvchi kabi ishlatadigan test vositasi. Test @playwright/test dan: test("…", async ({ page }) => { await page.goto("/"); … }). Elementlar lokator bilan topiladi — page.getByRole("button", { name: "Saqlash" }); Playwright bosishdan oldin element ko'rinishi, harakatsiz va yoqilgan bo'lishini o'zi kutadi (auto-wait). Tekshiruv await expect(lokator).toBeVisible() — 5 soniyagacha qayta uriniladi (web-first assertion). webServer sozlamasi testdan oldin ilovani o'zi build qilib ishga tushiradi.

Bu darsda

  • npm init playwright bilan loyiha ochib, yaratilgan fayllarni o'qiysiz.
  • Lokatorlar, strict mode va exact bilan elementni aniq topasiz.
  • Auto-wait va web-first assertion qanday ishlashini, isVisible() nega tuzoq ekanini tushuntirasiz.
  • webServer bilan production build ustida test yozasiz; page.route bilan soxta API bilan tanishasiz.
  • UI mode, --debug, codegen va HTML hisobot nima uchun kerakligini bilasiz.

Oldin bilishingiz kerak: Nega test yozamiz, RTL I: so'rovlar, Vitest Browser Mode, Vite build va preview.

1. Nega bu kerak?

O'tgan darsda komponentni haqiqiy Chrome'da sinadik. Lekin u baribir bitta komponent edi, test o'zi chizgan. Foydalanuvchi esa komponentni ko'rmaydi — u butun ilovani ochadi. Ilova esa ko'p qavat: Vite build, router, tarjimalar, service worker, CSS, server bilan aloqa.

Bu qavatlar orasidagi xatoni komponent testi ko'rmaydi. Router noto'g'ri yo'naltirsa, build'da fayl yo'qolsa, ruscha sahifada havola buzilsa — faqat foydalanuvchi biladi. Coverage hisobotida sahifalar 0 % edi — aynan shu bo'shliq.

17-qismdan beri vazifalar ni har qadamda biz — kurs mualliflari — o'z skriptlarimiz bilan brauzerda tekshirib keldik ("brauzer tekshiruvi 159"). Endi bu ish loyihaning o'zida bo'ladi: siz yozasiz, siz yurgizasiz.

E2E test (Nega test yozamiz darsida tanishgan edik) — ilovani boshidan oxirigacha, foydalanuvchi yo'li bo'yicha sinash. O'xshatish: restoran inspektori oshxonadagi har pichoqni emas, mehmon sifatida kelib, buyurtma berib, ovqatni yeb ko'radi.

2. Playwright nima

Playwright — Microsoft chiqargan brauzer boshqarish vositasi (playwright.dev). Ikki qismi bor:

  • playwright — kutubxona: brauzerni ochish, sahifaga o'tish, bosish. Browser Mode aynan shunga tayangan edi.
  • @playwright/test — test vositasi (test runner): test, expect, parallel ishlash, hisobot, qayta urinish.

Uchta brauzer dvigateli bilan ishlaydi: Chromium (Chrome va Edge asosi), Firefox va WebKit (Safari asosi). Brauzer mosligi darsida aytgan edik: Playwright ularni o'zi bilan olib keladi. Bitta test uchala dvigatelda ishlashi mumkin.

Har test yangi brauzer konteksti (browser context) da boshlanadi. Kontekst — inkognito oynaga o'xshaydi: o'z cookie'lari, localStorage i, keshi. Bitta test qoldirgan narsa keyingisiga o'tmaydi.

flowchart TB
  R["npx playwright test"] --> W["webServer: build + preview"]
  R --> P1["ishchi 1"]
  R --> P2["ishchi 2"]
  P1 --> C1["kontekst → sahifa → test A"]
  P2 --> C2["kontekst → sahifa → test B"]
  C1 --> W
  C2 --> W

Rasmga qarang: test vositasi avval ilovani ishga tushiradi, keyin testlarni bir nechta ishchi (worker) ga — parallel jarayonlarga — bo'lib beradi. Har test o'z toza kontekstida ilovaga ulanadi.

3. Boshlash: npm init playwright

3.1 Loyiha ochish

Playwright'ning rasmiy boshlovchisi savollar beradi (TypeScript yoki JS, test papkasi, GitHub Actions). Biz javoblarni bayroq bilan berdik va brauzer yuklashni o'chirdik — bo'sh papkada:

bash
npm init playwright@latest -- --quiet --lang=ts --browser=chromium --no-browsers --gha
text
Getting started with writing end-to-end tests with Playwright:
Initializing project in '.'
Installing Playwright Test (npm install --save-dev @playwright/test)…

added 3 packages, and audited 4 packages in 2s

found 0 vulnerabilities
Installing Types (npm install --save-dev @types/node)…
…
Writing playwright.config.ts.
Writing .github\workflows\playwright.yml.
Writing tests\example.spec.ts.
Writing package.json.
✔ Success! Created a Playwright Test project at C:\Users\…\init-pw

Chiqish oxirida foydali buyruqlar ro'yxati bor (npx playwright test, --ui, --debug, codegen) — ularni pastda ko'ramiz. Yaratilgan fayllar:

Fayl Nima
playwright.config.ts sozlama: test papkasi, brauzerlar, hisobot
tests/example.spec.ts namuna test (playwright.dev saytini ochadi)
.github/workflows/playwright.yml CI fayli — Playwright II da o'zimiznikini yozamiz
package.json @playwright/test qo'shildi

2026-10-20 holatiga @playwright/test ning eng yangi versiyasi 1.64.1 (14-oktabr). Kanon esa 1.64.0 da (semver: patch farqi). Shablon yozgan workflow actions/checkout@v4 ishlatadi — bu eski versiya. GitHub Actions darsida @v7 ni ishlatgan edik. Shablon har doim ham eng yangisini yozmaydi — tekshiring.

3.2 Brauzerlar qayerdan keladi

npx playwright install Playwright'ning shu versiyasiga moslangan brauzerlarni yuklaydi. Nima yuklanishini --dry-run ko'rsatadi (qisqartirilgan):

text
Chrome for Testing 156.0.8078.4 (playwright chromium v1248)
  Install location:    C:\Users\…\AppData\Local\ms-playwright\chromium-1248

Chrome Headless Shell 156.0.8078.4 (playwright chromium-headless-shell v1248)
  Install location:    C:\Users\…\AppData\Local\ms-playwright\chromium_headless_shell-1248

Firefox 157.0 (playwright firefox v1555)
…
WebKit 27.2 (playwright webkit v2370)
…

Brauzerlar loyiha ichiga emas, foydalanuvchi papkasidagi umumiy keshga (ms-playwright) tushadi. Kanon esa Browser Mode dagi kabi hech narsa yuklamadi — channel: "chrome" bilan kompyuterdagi Google Chrome ishlatiladi.

4. Birinchi test: vazifa qo'shish

Kanondagi e2e/asosiy.spec.ts ning birinchi testi:

ts
test("qo'shish: Enter — qator paydo bo'ladi, POST ketadi", async ({
  page,
}) => {
  const server = await soxtaApi(page);
  await page.goto("/");
  const maydon = page.getByRole("textbox", { name: "Yangi vazifa" });
  await maydon.fill("Choy damlash");
  await maydon.press("Enter");
  await expect(
    page.getByRole("checkbox", { name: "Choy damlash" }),
  ).toBeVisible();
  await expect(maydon).toHaveValue("");
  await expect(maydon).toBeFocused();
  // expect.poll — oddiy qiymatni (bu yerda — server holati)
  // kutadi
  await expect.poll(() => server.sorovlar).toContain("POST");
  expect(server.tanalar).toContain('POST {"matn":"Choy damlash"}');
});

Qatorma-qator:

  • test va expect — @playwright/test dan (Vitest'dan emas, lekin juda o'xshash).
  • async ({ page }) — test funksiyasi argument sifatida tayyor page oladi: yangi kontekstdagi bo'sh sahifa. Bunday tayyor narsalar fixture deyiladi — Playwright II da o'zimiznikini yozamiz.
  • soxtaApi(page) — server o'rnida soxta API («Soxta API» bo'limida).
  • page.goto("/") — sahifani ochish. To'liq manzil emas: baseURL konfiguratsiyada.
  • fill("Choy damlash") — maydonni tozalab, matn yozadi. press("Enter") — klaviatura tugmasi.
  • await expect(...).toBeVisible() — yangi qator chiqqanini kutib tekshiradi.
  • toHaveValue(""), toBeFocused() — maydon tozalandi va fokus unda qoldi: Bobur keyingi vazifani darhol yoza oladi.

Butun to'plamni yurgizamiz (biz yurgizdik, holat 09):

bash
npx playwright test

Qatorlar tartibiga va boshidagi raqamlarga qarang — ular nega 3, 4, 1, 5, 2 bo'lib ketdi?

text
Running 5 tests using 5 workers

  ok 3 [chrome] › e2e\asosiy.spec.ts:36:1 › tahrir: forma → PATCH, vazifa sahifasiga qaytadi (2.0s)
  ok 4 [chrome] › e2e\asosiy.spec.ts:18:1 › qo'shish: Enter — qator paydo bo'ladi, POST ketadi (2.1s)
  ok 1 [chrome] › e2e\asosiy.spec.ts:55:1 › oflayn: o'zgarish navbatda, internet qaytganda yuboriladi (2.1s)
  ok 5 [chrome] › e2e\asosiy.spec.ts:104:1 › eksport: «Faylga yuklab olish» — JSON fayl, ichida ro'yxat (2.3s)
  ok 2 [chrome] › e2e\asosiy.spec.ts:87:1 › til: /ru/ — ruscha; sozlamalarda English — /en/sozlamalar (2.7s)

  5 passed (11.4s)

[chrome] — loyiha (project) nomi. Testlar e'lon tartibida emas, tugash tartibida chiqadi: 5 ta ishchi ularni bir vaqtda yurgizdi. Raqam (ok 3) — test tartib raqami. Sizda vaqtlar boshqacha bo'ladi.

5. Lokatorlar

5.1 Oila va ustuvorlik

Playwright lokatorlari Browser Mode dagilar kabi "qidiruv yo'riqnomasi": qidiruv harakat paytida bo'ladi. Ustuvorlik RTL dagidek:

Lokator Qachon
getByRole("button", { name }) deyarli hamma narsa — birinchi tanlov
getByLabel("Ismingiz") forma maydoni (RTL'da getByLabelText)
getByPlaceholder, getByText label yoki rol bo'lmasa
getByAltText, getByTitle rasm, title
getByTestId("…") oxirgi chora (data-testid)
locator("#aloqa") CSS selektor — kanon faqat id li xabar hududlari uchun

Farq bitta: Playwright rol va nomni W3C ARIA qoidalari bo'yicha ekran o'quvchi ko'rganidek hisoblaydi (hujjatda: "foydalanuvchi va yordamchi texnologiya sahifani qanday qabul qilsa"). Ekran o'quvchidan yashirin elementlar (display: none, aria-hidden="true") sukut bo'yicha o'tkazib yuboriladi. Bir nozik farq: Browser Mode lokatori inert orqa fondagi tugmani ham "topgan" edi — u yerda tekshiruvni fokus orqali qildik.

5.2 Strict mode: bitta lokator — bitta element

Harakat yoki tekshiruv paytida lokator aniq bitta elementga mos kelishi shart. Ikkitasi topilsa — xato. Bu strict mode (qat'iy rejim). Biz statistika sahifasida sarlavhani qidirdik:

ts
await page.goto("/statistika");
await expect(
  page.getByRole("heading", { name: "Statistika" }),
).toBeVisible();
text
    Error: expect(locator).toBeVisible() failed

    Locator: getByRole('heading', { name: 'Statistika' })
    Expected: visible
    Error: strict mode violation: getByRole('heading', { name: 'Statistika' }) resolved to 2 elements:
        1) <h2 tabindex="-1">Statistika</h2> aka getByRole('heading', { name: 'Statistika', exact: true })
        2) <h2 id="_r_4_">…</h2> aka getByRole('heading', { name: 'Batafsil statistika' })

Tarjima: "qat'iy rejim buzildi: lokator 2 ta elementga mos keldi". Nega «Batafsil statistika» ham? name sukut bo'yicha qism bo'yicha va katta-kichik harfga qaramay solishtiriladi: "statistika" so'zi ikkala nomda ham bor. Xabarning o'zi yechim taklif qiladi — aka dan keyin har element uchun aniq lokator. Birinchisiga exact: true qo'shilgan: endi nom to'liq mos kelishi kerak.

Kanon birinchi o'tishda aynan shu xatoga uchradi — kirish testida sarlavha { level: 2, name: "Statistika", exact: true } shundan.

Nega xato yaxshi? Strict mode bo'lmasa, Playwright birinchisini bosardi — balki noto'g'risini. Test "o'tardi", lekin boshqa narsani tekshirardi.

5.3 Toraytirish

Element bir guruh ichida bo'lsa — lokatorni lokator ichidan chaqiring (within ning Playwright ko'rinishi):

ts
const dialog = page.getByRole("dialog", {
  name: "Vazifa o'chirilsinmi?",
});
await dialog.getByRole("button", { name: "Bekor qilish" }).click();

// Ro'yxatdan "Non olish" bor qatorni topib, ichidagi tugma
await page
  .getByRole("listitem")
  .filter({ hasText: "Non olish" })
  .getByRole("button", { name: /O'chirish/ })
  .click();

filter({ hasText }) — faqat shu matnni o'z ichiga olgan elementlar. first(), nth(2) ham bor, lekin ehtiyot bo'ling: tartib o'zgarsa, test boshqa elementni bosadi.

Tekshirib ko'ring: Sahifada «Saqlash» va «Saqlash va chiqish» tugmalari bor. page.getByRole("button", { name: "Saqlash" }).click() nima qiladi?

Javob

Strict mode xatosi: nom qism bo'yicha solishtiriladi, ikkala tugma ham mos keladi. Playwright tavakkal bosmaydi. Tuzatish — { name: "Saqlash", exact: true } yoki tugmalarni ajratadigan konteyner ichidan qidirish.

6. Auto-wait va web-first assertion

6.1 Auto-wait: bosishdan oldin tekshiruv

click() darhol bosmaydi. Playwright avval harakatga tayyorlikni (actionability) tekshiradi va hammasi bajarilguncha kutadi:

Tekshiruv Ma'nosi
Visible ko'rinadi (o'lchami bor, visibility: hidden emas)
Stable harakatlanmayapti (animatsiya tugagan)
Receives Events bosish nuqtasida aynan shu element turadi — ustini toast yoki boshqa oyna yopmagan
Enabled disabled emas

Jadval Playwright hujjatidan ("Actionability", click uchun to'rt tekshiruv). fill boshqacha to'plamni tekshiradi: Visible, Enabled va Editable (tahrirlanadi, readonly emas). Bundan oldin esa strict mode qoidasi ishlaydi: lokator aniq bitta elementga mos kelishi kerak. Shuning uchun e2e testlarda sleep(500) yozilmaydi — animatsiya tugashini Playwright o'zi kutadi.

O'xshatish: ehtiyotkor haydovchi. Svetofor yashil bo'ldi, piyoda o'tib bo'ldi, oldindagi mashina qo'zg'aldi — shundan keyin yuradi. Soat bo'yicha "3 soniyadan keyin yuraman" demaydi.

6.2 Web-first assertion

await expect(lokator).toBeVisible() — web-first assertion (sahifaga mos tekshiruv): shart bajarilguncha qayta tekshiradi, sukut bo'yicha 5 soniya. Element umuman chiqmasa:

text
    Error: expect(locator).toBeVisible() failed

    Locator: getByRole('button', { name: 'Yuborish' })
    Expected: visible
    Timeout: 5000ms
    Error: element(s) not found

    Call log:
      - Expect "toBeVisible" getByRole('button', { name: 'Yuborish' }) with timeout 5000ms
      - waiting for getByRole('button', { name: 'Yuborish' })

Tarjima: "5 000 ms ichida element topilmadi". Call log — Playwright nima qilganining kundaligi. Butun test uchun alohida chegara bor — sukut bo'yicha 30 s.

flowchart LR
  A["expect(lokator).toBeVisible()"] --> B{"shart bajarildimi?"}
  B -- ha --> C["✓ davom"]
  B -- "yo'q" --> D{"5 s o'tdimi?"}
  D -- "yo'q" --> E["biroz kut"] --> B
  D -- ha --> F["× Timeout: 5000ms"]

6.3 Tuzoq: isVisible() — bir martalik surat

locator.isVisible() qayta urinmaydi — u shu lahzadagi holatni qaytaradi. Biz oflayn testida shunday yozib ko'rdik: internet qaytgach, navbat xabari yo'qolishi kerak:

ts
await context.setOffline(false);
expect(await page.locator("#navbat").isVisible()).toBe(false);
text
    Error: expect(received).toBe(expected) // Object.is equality

    Expected: false
    Received: true
Qadam #navbat xabari isVisible() await expect(...).toBeHidden()
setOffline(false) hali ko'rinadi darhol true → test yiqildi kutishni boshladi
ilova navbatni yubordi yo'qoldi — (allaqachon tugagan) shart bajarildi → davom

Ilova navbatni yuborib ulgurmagan edi — xabar hali ko'rinib turardi. Kanon to'g'ri yozgan: await expect(page.locator("#navbat")).toBeHidden() — yo'qolguncha kutadi. Bunday "ba'zan o'tadi, ba'zan yo'q" test — flaky test, u Test strategiyasi darsining asosiy mavzusi.

G'oyani natija oynasida ko'ramiz. Bu yerda Playwright yo'q, lekin farq o'sha: "bir martalik surat" va "qayta urinish". «Ishga tushir» ni bosing:

jsx
import { render, screen } from "@testing-library/react";
import { useEffect, useState } from "react";
import { expect, test, vi } from "vitest";

function SaveStatus() {
  const [saved, setSaved] = useState(false);
  useEffect(() => {
    const id = setTimeout(() => setSaved(true), 300);
    return () => clearTimeout(id);
  }, []);
  return <p role="status">{saved ? "Saqlandi" : "Saqlanmoqda…"}</p>;
}

test("bir martalik surat — shoshildi", () => {
  render(<SaveStatus />);
  const text = screen.getByRole("status").textContent;
  expect(text).toBe("Saqlandi");
});

test("qayta urinish — kutdi", async () => {
  render(<SaveStatus />);
  await vi.waitFor(() =>
    expect(screen.getByRole("status")).toHaveTextContent("Saqlandi"),
  );
});

Konsolda:

text
  × bir martalik surat — shoshildi
    → expected 'Saqlanmoqda…' to be 'Saqlandi' // Object.is equality
  ✓ qayta urinish — kutdi
Testlar: 1 yiqildi, 1 o'tdi (jami 2)

Birinchi test holatni bir marta o'qidi — 300 ms hali o'tmagan edi. Ikkinchisi vi.waitFor bilan qayta-qayta tekshirdi. Playwright'ning await expect(lokator)… — aynan ikkinchisi, faqat waitFor siz.

6.4 expect.poll — sahifadan tashqaridagi qiymat

Web-first assertion lokator bilan ishlaydi. Soxta serverga so'rov ketganini tekshirish kerak bo'lsa-chi? U sahifada emas, test kodidagi massivda. Buning uchun — expect.poll(() => qiymat): funksiyani qayta-qayta chaqirib, natijani tekshiradi. Kanonda: await expect.poll(() => server.sorovlar).toContain("POST").

Tekshirib ko'ring: Nega await page.waitForTimeout(1000) (1 soniya kutish) yomon yechim, await expect(lokator).toBeHidden() esa yaxshi?

Javob

Qat'iy kutish ikki tomondan yomon: tez kompyuterda 1 s behuda yo'qotiladi, sekin CI'da esa 1 s yetmay, test tasodifan yiqiladi. Web-first assertion shart bajarilgan zahoti davom etadi va 5 s gacha sabr qiladi — ham tez, ham barqaror.

7. webServer: test oldidan ilovani ishga tushirish

Kanonning playwright.config.ts i (holat 09, izohlar qisqartirilgan):

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

const CI = process.env["CI"] !== undefined;
const PORT = 4173;

export default defineConfig({
  testDir: "e2e",
  fullyParallel: true,
  // CI'da test.only qolib ketsa — yiqiladi
  forbidOnly: CI,
  reporter: [["list"], ["html", { open: "never" }]],
  use: {
    baseURL: `http://localhost:${PORT}`,
    // Ilovaning sw.js i so'rovlarni o'zi oladi —
    // page.route ularni ko'rmaydi
    serviceWorkers: "block",
    // Tizimdagi Chrome
    channel: "chrome",
  },
  projects: [
    {
      name: "chrome",
      use: { ...devices["Desktop Chrome"] },
    },
  ],
  webServer: {
    command: `npm run build && npm run preview -- --port ${PORT} --strictPort`,
    url: `http://localhost:${PORT}`,
    reuseExistingServer: !CI,
    timeout: 180_000,
  },
});
Kalit Nima qiladi
testDir testlar e2e/ da (Vitest'nikidan alohida)
fullyParallel bitta fayl ichidagi testlar ham parallel
forbidOnly CI'da test.only — xato (Vitest I dagi only tuzog'i)
reporter terminalda list, yana playwright-report/ da HTML
baseURL page.goto("/") shu manzilga qo'shiladi
serviceWorkers: "block" ilovaning SW'i o'chiriladi
devices["Desktop Chrome"] oyna o'lchami, user agent — tayyor to'plam
webServer.command testdan oldin: build va preview
reuseExistingServer lokalda ishlab turgan server bo'lsa — o'shani oladi

Uch qarorga diqqat qiling:

  • Production build ustida. npm run dev emas, build && preview: foydalanuvchi oladigan aynan o'sha fayllar. Dev serverda bo'lmagan xato (masalan, kod bo'lish yoki minifikatsiya) shu yerda chiqadi.
  • serviceWorkers: "block". Ilovaning service worker i so'rovni o'zi ushlasa, page.route ularni ko'rmaydi. SW'ning o'zi alohida sinaladi.
  • channel yuqori use da. Kanon tuzog'i (o'lchandi): channel faqat bitta loyihada yozilsa, boshqa loyiha Playwright'ning o'z brauzerini qidiradi va yiqiladi: Executable doesn't exist at …\ms-playwright\chromium_headless_shell-1248\… npx playwright install.

8. Soxta API: page.route bilan birinchi tanishuv

E2E testga haqiqiy server kerakmi? Kanon uni ishlatmaydi: internetga bog'liq test sekin va beqaror bo'ladi, server ma'lumoti esa testlar orasida aralashib ketadi. O'rniga — brauzer tarmoq qatlamida soxta server. Ilova kodi buni bilmaydi: u oddiy fetch qiladi.

ts
  await page.route("**/api/mashq/vazifalar/**", async (route) => {
    const sorov = route.request();
    const metod = sorov.method();
    // … so'rovni yozib qo'yamiz (s.sorovlar, s.tanalar)
    if (metod === "GET") {
      return route.fulfill({
        json: { versiya: s.versiya, vazifalar: s.vazifalar },
      });
    }
    // … POST qo'shadi, PATCH o'zgartiradi, DELETE o'chiradi
  });
  • page.route(naqsh, handler) — shu manzilga ketadigan har so'rovni ushlaydi. ** — "istalgan qism".
  • route.fulfill({ json }) — serverga bormasdan javob qaytaradi.
  • Xulq MSW dagi soxta server bilan bir xil: holatli (POST qo'shgani keyingi GET'da ko'rinadi).

Xatolar, uzilish, sekin server — Playwright II da.

9. Kanon testlaridan uch usul

Oflayn. context.setOffline(true) — butun kontekst uchun internetni uzadi (Oflayn ishlash):

ts
  await context.setOffline(true);
  await expect(page.locator("#aloqa")).toHaveText(
    "Internet yo'q. Ro'yxat — shu brauzerdagi nusxa.",
  );

Keyin vazifa qo'shiladi, navbat xabari chiqadi, POST ketmaydi. setOffline(false) — navbat yuboriladi. Bu oqimni jsdom'da sinab bo'lmaydi.

Til. /ru/ ochiladi, html ning lang atributi tekshiriladi, sozlamalarda English tanlanadi — manzil /en/sozlamalar. i18n butun yo'l bo'yicha ishlashi shu tarzda ko'rinadi.

Yuklab olish. Hodisani bosishdan oldin kutishni boshlash kerak:

ts
  const yuklash = page.waitForEvent("download");
  await page
    .getByRole("button", { name: "Faylga yuklab olish" })
    .click();
  const fayl = await yuklash;
  expect(fayl.suggestedFilename()).toMatch(/\.json$/);

Avval click, keyin waitForEvent yozilsa, hodisa bosish paytida sodir bo'lib, kutuvchi uni o'tkazib yuborishi mumkin. Promise'ni saqlab, keyin await — shu muammoning yechimi.

10. UI mode, debug, codegen va hisobot

Test yiqilsa yoki yangi test yozish kerak bo'lsa, Playwright'da to'rt asbob bor:

Buyruq Nima qiladi
npx playwright test --ui UI mode: testlar ro'yxati, har qadam vaqt chizig'ida, DOM surati, kuzatuv rejimi
npx playwright test --debug Inspector: qadamma-qadam, har harakatdan oldin to'xtaydi
npx playwright codegen localhost:4173 codegen: siz bosasiz, u test kodini yozadi
npx playwright show-report HTML hisobot: yiqilgan test, xato, rasm
  • UI mode — test yozishda eng qulayi. Har qadamni bosib, o'sha paytdagi sahifani ko'rasiz.
  • --debug va kod ichidagi await page.pause() — test o'sha joyda to'xtaydi, brauzer oynasi ochiq qoladi.
  • codegen — tayyor lokatorlarni (getByRole …) taklif qiladi. Uning kodi — qoralama: tekshiruvlarni (expect) va tushunarli nomlarni o'zingiz qo'shasiz.
  • --headed — brauzer oynasini ko'rsatib yurgizish.

Bu oynalar grafik interfeys — ularni o'z kompyuteringizda oching. Yana bir-ikki foydali bayroq:

bash
npx playwright test --list
text
Listing tests:
  [chrome] › asosiy.spec.ts:18:1 › qo'shish: Enter — qator paydo bo'ladi, POST ketadi
  [chrome] › asosiy.spec.ts:36:1 › tahrir: forma → PATCH, vazifa sahifasiga qaytadi
…

-g "oflayn" — faqat nomida shu so'z bor testlar; e2e/asosiy.spec.ts:55 — bitta test qatori bo'yicha; --project=chrome — bitta loyiha; --repeat-each=5 — har testni 5 marta (beqarorlikni tutish uchun).

Har yiqilgan test uchun test-results/<test>/error-context.md ham yoziladi: xato matni va sahifaning a11y surati — yordamchi vosita (yoki hamkasb) xatoni tez tushunishi uchun.

11. Vazifalar qadami: Playwright e2e (holat 09)

11.1 O'rnatish va fayllar

bash
git switch -c test/e2e
npm i -D -E @playwright/test@1.64.0

Kanon: +1 paket, 40 KB — playwright Browser Mode da kelgan edi. Brauzer yuklanmadi.

Fayl Nima
playwright.config.ts «webServer» bo'limidagi sozlama
e2e/soxta-api.ts page.route bilan holatli soxta server
e2e/asosiy.spec.ts 5 test: qo'shish, tahrir, oflayn, til, eksport
tsconfig.e2e.json e2e/ va config uchun: Node + DOM
package.json "test:e2e": "playwright test"

Har testda avval kirish formasi to'ldiriladi (test.beforeEach) — ilova kirmagan foydalanuvchini /kirish ga yo'naltiradi. Bu sekin; Playwright II da bir marta kirib, holatni saqlaymiz.

tsconfig.e2e.json nega kerak? E2E testlar Node'da ishlaydi, lekin Playwright turlari DOM'ga tayanadi — Browser Mode dagi TS2304 muammosining o'zi. tsconfig.json ning references iga qo'shildi: npm run tip testlarni ham tekshiradi.

playwright-report/, test-results/, e2e/.auth/ — .gitignore, ESLint va Prettier e'tiboridan chiqarildi.

11.2 Birinchi o'tishdagi xatolar

Kanon testlarni birinchi yurgizganda ikki xato chiqdi — ikkalasi ham test xatosi, ilova to'g'ri edi:

  • statistika sarlavhasi — strict mode («Strict mode» bo'limidagi xabar) → exact: true;
  • til tanlovi ro'yxat sahifasida emas, sozlamalarda ekan → test sozlamalar havolasini bosadi.

knip ham bitta narsani topdi: soxta-api.ts dagi ishlatilmagan BOSHLANGICH eksporti — olib tashlandi.

11.3 Natija va commit

  • E2E: 5 test, 12.1 s devor soati (shundan build + preview ~2 s; testlar 10.2 s, 6 ishchi).
  • Vitest 348, brauzer 6 — o'zgarmadi. Coverage 62.72 % — o'zgarmadi: e2e testlar test:qamrov ga kirmaydi.
  • Ilova JS — 206.7 KB gzip, o'zgarmadi. Lint, format:check, tip, knip — toza.
bash
git add -A
git commit

Commit (kanon): test: Playwright e2e — qo'shish, tahrir, oflayn navbat, til, eksport (production build ustida).

12. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
strict mode violation … resolved to 2 elements nom qism bo'yicha mos exact: true yoki konteyner ichidan qidirish
Timeout: 5000ms … element(s) not found nom yoki sahifa boshqa Call log, UI mode, error-context.md
ba'zan o'tadi, ba'zan yo'q isVisible(), waitForTimeout web-first assertion: await expect(...)
Executable doesn't exist … npx playwright install brauzer yuklanmagan npx playwright install yoki channel: "chrome"
Process from config.webServer was not able to start build yiqildi (masalan tip xatosi) npm run build ni alohida yurgizing
page.route so'rovni ko'rmaydi ilovaning SW'i ushlagan serviceWorkers: "block"
yuklab olish kutilmaydi waitForEvent bosishdan keyin avval Promise, keyin click

13. Mashqlar

Mashqlar kurs/mashqlar/22/09-playwright-e2e/ da. Testlarni vazifalar loyihangizning e2e/ papkasiga qo'yib, npx playwright test bilan yurgizing (natija oynasida Playwright yo'q).

1-mashq (oson): bo'sh joylarni to'ldiring

  • Test fayli importi: import { expect, test } from ""
  • Nom to'liq mos kelsin: getByRole("heading", { name: "Statistika", : true })
  • Element yo'qolguncha kutish: await expect(lokator).()
  • Sahifadan tashqari qiymatni kutish: await expect.(() => server.sorovlar).toContain("POST")
  • Interaktiv rejim: npx playwright test --
Yechim
  • @playwright/test — test vositasi (playwright — faqat kutubxona).
  • exact — sukut bo'yicha nom qism bo'yicha va katta-kichik harfga qaramay solishtiriladi.
  • toBeHidden — web-first: yo'qolguncha 5 s gacha kutadi; isVisible() esa bir martalik.
  • poll — funksiyani qayta-qayta chaqiradi.
  • ui — UI mode: vaqt chizig'i va DOM suratlari.

2-mashq (o'rta): Bobur va buyruqlar oynasi

Fokus boshqaruvi darsida va'da qilgan edik: Bobur ssenariysini Playwright'da page.keyboard va :focus bilan yozamiz. Test: «Yangi vazifa» maydoniga fokus qo'ying, Control+K bosing — «Buyruqlar» dialogi ko'rinadi va undagi qidiruv maydoni (combobox) fokusda. Escape — dialog yo'qoladi, fokus yana «Yangi vazifa» da. Oxirida page.locator(":focus") ning kirish nomini tekshiring.

Ishora: page.keyboard.press("Control+K"); fokusni tekshirish — toBeFocused(); soxtaApi ni har testda ulang.

Yechim
ts
import { expect, test } from "@playwright/test";
import { soxtaApi } from "./soxta-api.ts";

test("Ctrl+K — buyruqlar oynasi, Escape — fokus qaytadi", async ({
  page,
}) => {
  await soxtaApi(page);
  await page.goto("/");
  const maydon = page.getByRole("textbox", { name: "Yangi vazifa" });
  await maydon.focus();
  await page.keyboard.press("Control+K");
  const oyna = page.getByRole("dialog", { name: "Buyruqlar" });
  await expect(oyna).toBeVisible();
  // page.getByRole("combobox") — 2 ta (sahifadagi <select> ham
  // combobox): strict mode xatosi. Oyna ichidan qidiramiz
  await expect(oyna.getByRole("combobox")).toBeFocused();
  await page.keyboard.press("Escape");
  await expect(oyna).toBeHidden();
  await expect(maydon).toBeFocused();
  // 18-qismdagi usul: fokusdagi element — :focus
  await expect(page.locator(":focus")).toHaveAccessibleName(
    "Yangi vazifa",
  );
});

Biz avval page.getByRole("combobox") deb yozdik — test yiqildi: strict mode violation … resolved to 2 elements, ikkinchisi — <select id="saralash"> (aka getByLabel('Tartib')). <select> ning roli ham combobox. Browser Mode da bu muammo yo'q edi: u yerda faqat bitta komponent chizilgan edi. E2E'da butun sahifa bor — lokatorni oyna ichiga toraytirish shart. To'g'ri variant o'tdi (1.6 s).

3-mashq (qiyin): o'chirish oqimi

Bitta testda: «O'chirish: Non olish #bozor» tugmasi → dialog «Vazifa o'chirilsinmi?» → «Bekor qilish» → dialog yo'q, qator joyida, DELETE ketmagan. Keyin yana bosing → «O'chirish» → qator yo'q va soxta serverga DELETE /1 ketdi.

Ishora: dialog ichidagi «O'chirish» tugmasi uchun exact: true (tashqaridagi «O'chirish: …» tugmalari ham mos keladi); server holati — expect.poll.

Yechim
ts
import { expect, test } from "@playwright/test";
import { soxtaApi } from "./soxta-api.ts";

test("o'chirish: bekor qilish va tasdiqlash", async ({ page }) => {
  const server = await soxtaApi(page);
  await page.goto("/");
  const qator = page.getByRole("checkbox", {
    name: "Non olish #bozor",
  });
  const tugma = page.getByRole("button", {
    name: "O'chirish: Non olish #bozor",
  });
  const dialog = page.getByRole("dialog", {
    name: "Vazifa o'chirilsinmi?",
  });

  await tugma.click();
  await expect(dialog).toBeVisible();
  await dialog.getByRole("button", { name: "Bekor qilish" }).click();
  await expect(dialog).toBeHidden();
  await expect(qator).toBeVisible();
  expect(server.sorovlar).not.toContain("DELETE /1");

  await tugma.click();
  await dialog
    .getByRole("button", { name: "O'chirish", exact: true })
    .click();
  await expect(qator).toBeHidden();
  await expect.poll(() => server.sorovlar).toContain("DELETE /1");
});

Biz yurgizdik — o'tdi (1.7 s). Lokatorlar test boshida bir marta yozildi va ikki marta ishlatildi: ular "manzil", element emas. Dialog yopilib qayta ochilganda ham o'sha lokator yangi holatni topadi.

4-mashq: Vazifalar qadami

test/e2e branch'ida «Vazifalar qadami» dagi fayllarni yarating va npm run test:e2e ni yurgizing. Keyin UI mode'ni oching (npx playwright test --ui) va «oflayn» testini qadamma-qadam ko'ring: setOffline(true) dan keyin sahifada nima o'zgaradi?

Yechim

Tekshiruv ro'yxati:

  • npm run test:e2e — 5 passed; birinchi marta build tufayli sekinroq.
  • Ilova ishlab turgan bo'lsa (npm run preview -- --port 4173), reuseExistingServer uni oladi — build qayta qilinmaydi.
  • «oflayn» testida vaqt chizig'ida «Internet yo'q…» xabari va «Navbatda: 1 ta o'zgarish…» qatori ko'rinadi; setOffline(false) dan keyin navbat qatori yo'qoladi.
  • npm run tip — toza (tsconfig.e2e.json testlarni ham tekshiradi). Bitta testda getByRole ga xato tur bering ({ name: 1 }) — tip ushlaydi.

14. Real ishda

  • E2E testlar kam, lekin eng muhim yo'llar uchun: kirish, to'lov, buyurtma, ro'yxatdan o'tish. Har tugma uchun e2e yozilmaydi — u trofey ning tepasi.
  • Playwright va Cypress — eng ko'p uchraydigan ikki e2e vositasi. Ikkalasining g'oyasi yaqin (lokator, kutish); Playwright bir nechta brauzer dvigateli va parallel ishlash bilan ajralib turadi.
  • Production build ustida sinash — real jamoalardagi odatiy talab: "dev serverda ishlaydi" yetmaydi.
  • Intervyu savollari: "auto-wait nima?", "nega waitForTimeout yomon?", "strict mode nima uchun kerak?", "e2e va komponent testining farqi?".

Xulosa

  • Playwright butun ilovani haqiqiy brauzerda sinaydi; har test — yangi toza kontekst, page fixture'i bilan.
  • Lokatorlar RTL ustuvorligida (getByRole birinchi); strict mode bir nechta moslikni xato qiladi — exact, toraytirish.
  • Auto-wait bosishdan oldin tayyorlikni kutadi; web-first assertion 5 s gacha qayta tekshiradi; isVisible() — bir martalik surat.
  • webServer testdan oldin production build'ni ko'taradi; page.route — soxta API.
  • UI mode, --debug, codegen, HTML hisobot — yozish va xato qidirish asboblari.
  • vazifalar: 5 e2e test, 12.1 s, +40 KB; ilova 206.7 KB — o'zgarmadi.

Keyingi dars: Playwright II: auth, tarmoq, trace va CI — bir marta kirib holatni saqlash, server xatolari va uzilishni sinash, trace viewer va GitHub Actions'da testlar.

Manbalar

  • Playwright: "Installation", "Writing tests", "Locators", "Actionability", "Assertions", "Test configuration", "Web server" (playwright.dev/docs)
  • Playwright: "Timeouts" — expect 5 s, test 30 s (playwright.dev/docs/test-timeouts)
  • Playwright: "UI Mode", "Debugging tests", "Test generator" (codegen)
  • create-playwright 1.17.139 (npm init playwright), @playwright/test 1.64.0 / 1.64.1 (npm, 2026-10-07 / 2026-10-14)
  • Haqiqiy chiqishlar: Playwright 1.64.0, Node 24.21.0, Chrome (channel "chrome", headless), Windows 11 (2026-10-20)
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Playwright I: e2e testlar asoslari — lokatorlar, auto-wait, web-first assertion va webServer — IlmHamroh