Azimjon Abdurasulov

QA 18: Playwright (1) — o'rnatish, locator'lar, action'lar, assertion'lar va auto-waiting

🧪 QA Engineer — Zero to Hero

QA 18: Playwright (1) — o'rnatish, locator'lar, action'lar, assertion'lar va auto-waiting

Holat: 2026-yil oktabr. Playwright 1.64.0 (@playwright/test), Node.js 24 LTS. Darsdagi barcha testlar (jami 23 ta) saucedemo.com, TodoMVC demo va Selenium'ning rasmiy demo sahifalarida Chromium'da ishga tushirib tekshirilgan. Xato xabarlari ham haqiqiy ishga tushirishdan olingan.

Bu darsdan keyin siz: noldan Playwright loyihasi yaratib, haqiqiy saytga barqaror E2E testlar yoza olasiz.

  • Playwright'ni o'rnatib, playwright.config.tsni tushunasiz;
  • to'g'ri locator tanlashni va strict mode xatosini tuzatishni bilasiz;
  • action va web-first assertion'lar bilan login, savatcha va checkout testlarini yozasiz;
  • auto-waiting qanday ishlashini va nega waitForTimeout yomonligini tushunasiz;
  • yiqilgan testni trace viewer, UI mode va Inspector bilan tahlil qilasiz.

Mundarija

  1. Playwright nima va qanday ishlaydi
  2. O'rnatish
  3. playwright.config.ts asoslari
  4. Testlarni ishga tushirish va codegen
  5. Birinchi test: saucedemo login
  6. Locator'lar
  7. Action'lar
  8. Assertion'lar
  9. Auto-waiting va nega waitForTimeout yomon
  10. Test tuzilmasi: describe, hook'lar va tag'lar
  11. To'liq misol: savatcha va checkout
  12. Debugging: trace, UI mode va Inspector
  13. Amaliy mashqlar
  14. O'zingizni tekshiring
  15. Xulosa
  16. Manbalar

1. Playwright nima va qanday ishlaydi {#nima}

Playwright — Microsoft'ning ochiq kodli (Apache-2.0) brauzer avtomatlashtirish framework'i. @playwright/test paketi esa unga test runner, assertion'lar, parallel ishga tushirish, hisobotlar va trace qo'shadi. Nega aynan Playwright tanlanganini QA 16 da muhokama qildik.

Test ishga tushganda nima bo'ladi:

npx playwright test
 └─ Test runner (@playwright/test) — test fayllarni topadi, worker jarayonlarga bo'ladi
     └─ Browser (Chromium / Firefox / WebKit) — worker'da ishga tushadi
         └─ BrowserContext — HAR BIR TEST uchun yangi "inkognito" profil
             └─ Page — brauzer tab'i: page.goto(), click(), fill() ...

Eng muhim tushuncha — test izolyatsiyasi. Rasmiy hujjat: "Playwright creates a context for each test, and provides a default Page in that context." Ya'ni har bir test o'zining cookie, localStorage va session'iga ega toza brauzer profilida boshlanadi. Natijada:

  • bitta test yiqilsa, keyingisiga ta'sir qilmaydi;
  • testlarni istalgan tartibda va parallel ishga tushirish mumkin;
  • bitta testni alohida, xohlagancha qayta ishga tushirib debug qilasiz.

Testga async ({ page }) => { ... } ko'rinishida beriladigan page — fixture. Playwright har test uchun tayyor obyektlarni beradi: page, context, browser, request (API uchun), browserName. O'z fixture'larimizni QA 19 da yozamiz.


2. O'rnatish {#ornatish}

Tizim talablari (rasmiy hujjat)

  • Node.js: 22.x, 24.x yoki 26.x (biz 24 LTS ishlatamiz — QA 17);
  • macOS 14 (Sonoma) va yangiroq; Windows 11+ yoki WSL; Debian 12/13, Ubuntu 22.04/24.04/26.04.

A usul: tayyor shablon — npm init playwright@latest

mkdir qa-demo-tests && cd qa-demo-tests
npm init playwright@latest

Buyruq bir nechta savol beradi:

SavolStandart javobBizning tanlov
TypeScript yoki JavaScriptTypeScriptTypeScript
Testlar papkasi nomitests (agar tests bor bo'lsa — e2e)tests
GitHub Actions workflow qo'shilsinmi—Ha (QA 21 da kerak bo'ladi)
Playwright brauzerlari o'rnatilsinmiHaHa

Natijada shu fayllar paydo bo'ladi:

qa-demo-tests/
├── playwright.config.ts        # test konfiguratsiyasi
├── package.json
├── package-lock.json
├── tests/
│   └── example.spec.ts         # namunaviy test (keyin o'chiramiz)
└── .github/workflows/playwright.yml   # agar GitHub Actions tanlangan bo'lsa

B usul: qo'lda o'rnatish

mkdir qa-demo-tests && cd qa-demo-tests
npm init -y
npm i -D @playwright/test @types/node@24
npx playwright install            # Chromium, Firefox va WebKit
# yoki faqat bittasi:  npx playwright install chromium

Tekshiramiz:

npx playwright --version   # Version 1.64.0

⚠️ Diqqat: Har bir Playwright versiyasi o'ziga mos brauzer build'larini ishlatadi. Shuning uchun yangilashda ikki qadam kerak: avval npm install -D @playwright/test@latest, keyin npx playwright install --with-deps. Linux CI serverlarda --with-deps brauzer uchun kerakli tizim kutubxonalarini ham o'rnatadi.

Kurs loyihamiz tuzilmasi (QA 16 da ko'rgandik): bu darsda playwright.config.ts va tests/ui/ dagi uchta faylni yozamiz — login.spec.ts, cart.spec.ts, checkout.spec.ts.


3. playwright.config.ts asoslari {#config}

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

export default defineConfig({
  testDir: "./tests",
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: "html",
  use: {
    baseURL: "https://www.saucedemo.com",
    testIdAttribute: "data-test",
    trace: "on-first-retry",
    screenshot: "only-on-failure",
    video: "retain-on-failure",
  },
  projects: [
    { name: "chromium", use: { ...devices["Desktop Chrome"] } },
    { name: "firefox", use: { ...devices["Desktop Firefox"] } },
    { name: "webkit", use: { ...devices["Desktop Safari"] } },
  ],
});
SozlamaMa'nosi
testDirTestlar qayerdan qidiriladi
fullyParallelBir fayl ichidagi testlar ham parallel ishlaydi
forbidOnlyCI'da test.only qolib ketsa — yiqitadi (hamma testlar o'tkazib yuborilmasin)
retriesYiqilgan testni qayta urinish soni: lokalda 0, CI'da 2
workersParallel jarayonlar soni (undefined — standart, CPU yadrolarining 50%)
reporterHisobot turi: list, html, junit...
use.baseURLpage.goto('/') shu manzilga qo'shiladi — muhit almashtirish oson
use.testIdAttributegetByTestId() qaysi atributni qidirishi. Standart — data-testid, saucedemo esa data-test ishlatadi
use.traceon-first-retry — trace faqat birinchi qayta urinishda yoziladi (rasmiy CI tavsiyasi)
use.screenshot / use.videoYiqilgan testning screenshot va videosini saqlaydi
projectsBir xil testlar turli brauzer/qurilmalarda

💡 Maslahat: projectsda uchta brauzer bor, lekin lokalda bittasini ishga tushirish mumkin: npx playwright test --project=chromium. Brauzerlararo farqlarni CI ko'rsatadi.


4. Testlarni ishga tushirish va codegen {#ishga-tushirish}

BuyruqNima qiladi
npx playwright testBarcha testlar, barcha project'lar
npx playwright test tests/ui/login.spec.tsBitta fayl
npx playwright test tests/ui/login.spec.ts:8Faqat 8-qatordagi test
npx playwright test -g "locked"Nomida "locked" bor testlar (katta-kichik harf farqlanmaydi)
npx playwright test --project=chromiumFaqat bitta project
npx playwright test --headedBrauzer oynasini ko'rsatib
npx playwright test --uiInteraktiv UI mode
npx playwright test --debugPlaywright Inspector bilan qadamma-qadam
npx playwright test --workers=1Ketma-ket (parallelsiz)
npx playwright test --repeat-each=10Har testni 10 marta — flaky testni ovlash uchun
npx playwright test --last-failedFaqat oldingi safar yiqilganlar
npx playwright test --listTestlarni ishga tushirmay, ro'yxatini ko'rsatadi
npx playwright test -xBirinchi yiqilishda to'xtaydi
npx playwright show-reportHTML hisobotni ochadi

Bu flag'lar npx playwright test --help chiqishidan (1.64.0) tekshirilgan. Tez-tez ishlatiladiganlarini package.jsonga qo'shing:

{
  "scripts": {
    "test": "playwright test",
    "test:ui": "playwright test --ui",
    "test:smoke": "playwright test --grep @smoke",
    "report": "playwright show-report"
  }
}

Codegen: brauzerda bosib, kod olish

npx playwright codegen --test-id-attribute=data-test https://www.saucedemo.com

Brauzer va Inspector oynasi ochiladi: siz saytda bosgan har bir harakat kodga aylanadi. Rasmiy hujjat bo'yicha codegen eng yaxshi locator'ni o'zi tanlaydi — role, matn va test id locator'larini ustun qo'yadi. Toolbar'da uchta tekshiruv tugmasi bor: assert visibility, assert text, assert value. -o tests/ui/new.spec.ts bilan natijani faylga saqlash mumkin.

⚠️ Diqqat: Codegen — boshlang'ich nuqta, tayyor test emas. U har bir bosishni yozib oladi, lekin test nomini, tekshiruvlarni (assertion), ma'lumotlarni va tuzilmani siz o'ylab topasiz. Generatsiya qilingan kodni doim qayta ko'rib chiqing va tozalang.


5. Birinchi test: saucedemo login {#birinchi-test}

Saucedemo — Sauce Labs'ning test mashqlari uchun yaratilgan demo do'koni. Barcha foydalanuvchilar uchun parol secret_sauce; standard_user oddiy, locked_out_user bloklangan, problem_user va performance_glitch_user esa ataylab "buzilgan" foydalanuvchilar.

tests/ui/login.spec.ts:

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

test.describe("Login", () => {
  test.beforeEach(async ({ page }) => {
    await page.goto("/");
  });

  test(
    "standard_user muvaffaqiyatli kiradi",
    { tag: "@smoke" },
    async ({ page }) => {
      // Arrange + Act
      await page.getByTestId("username").fill("standard_user");
      await page.getByTestId("password").fill("secret_sauce");
      await page.getByTestId("login-button").click();

      // Assert
      await expect(page).toHaveURL(/\/inventory\.html$/);
      await expect(page.getByTestId("title")).toHaveText("Products");
    },
  );

  test("locked_out_user bloklangani haqida xabar oladi", async ({ page }) => {
    await page.getByTestId("username").fill("locked_out_user");
    await page.getByTestId("password").fill("secret_sauce");
    await page.getByTestId("login-button").click();

    await expect(page.getByTestId("error")).toHaveText(
      "Epic sadface: Sorry, this user has been locked out.",
    );
    await expect(page).toHaveURL("/");
  });

  test("bo'sh forma: username majburiy", async ({ page }) => {
    await page.getByTestId("login-button").click();

    await expect(page.getByTestId("error")).toHaveText(
      "Epic sadface: Username is required",
    );
  });

  test("noto'g'ri parol: aniq xato xabari", async ({ page }) => {
    await page.getByTestId("username").fill("standard_user");
    await page.getByTestId("password").fill("wrong_password");
    await page.getByTestId("login-button").click();

    await expect(page.getByTestId("error")).toHaveText(
      "Epic sadface: Username and password do not match any user in this service",
    );
  });
});

Qatorma-qator:

  • import { test, expect } — test e'lon qilish va tekshirish funksiyalari.
  • test.describe('Login', ...) — testlarni guruhlash; hisobotda "Login ›" bo'lib ko'rinadi.
  • test.beforeEach — har bir testdan oldin login sahifasini ochadi.
  • page.goto('/') — baseURL bilan birlashib https://www.saucedemo.com/ ga boradi.
  • getByTestId('username') — data-test="username" atributli element (config'dagi testIdAttribute tufayli).
  • await expect(...).toHaveText(...) — matn kutilganiga teng bo'lguncha (standart 5 soniyagacha) qayta tekshiradi.
  • toHaveURL('/') — nisbiy URL ham baseURL bo'yicha tekshiriladi.

Ishga tushiramiz:

npx playwright test tests/ui/login.spec.ts --project=chromium --reporter=list
Running 4 tests using 4 workers

  ✓  2 [chromium] › tests/ui/login.spec.ts:36:7 › Login › noto'g'ri parol: aniq xato xabari (1.5s)
  ✓  3 [chromium] › tests/ui/login.spec.ts:30:7 › Login › bo'sh forma: username majburiy (1.5s)
  ✓  4 [chromium] › tests/ui/login.spec.ts:8:7 › Login › standard_user muvaffaqiyatli kiradi @smoke (1.5s)
  ✓  1 [chromium] › tests/ui/login.spec.ts:19:7 › Login › locked_out_user bloklangani haqida xabar oladi (2.1s)

  4 passed (2.6s)

To'rtta test 4 ta worker'da parallel ishladi — shuning uchun tartib aralash. Bu normal: testlar mustaqil.

🧠 Esda tuting: Xato matnlarini (Epic sadface: ...) saytdan aynan ko'chiring. "Taxminan shunday" yozilgan kutilgan natija — yiqiladigan test yoki, undan ham yomoni, noto'g'ri ma'lumotga asoslangan test.


6. Locator'lar {#locators}

Locator — elementni "qanday topish" haqidagi tavsif. U yaratilgan paytda elementni qidirmaydi: action yoki assertion bajarilayotganda qidiradi, kerak bo'lsa kutadi va qayta urinadi. Shuning uchun locator'ni o'zgaruvchiga saqlab, qayta ishlatish xavfsiz.

Locator tanlash tartibi: getByRole'dan XPath'gacha

Rasmiy hujjatning asosiy tavsiyasi: **foydalanuvchiga ko'rinadigan atributlar va aniq kontraktlarni (test id) ustun qo'ying, avvalo getByRole**. CSS va XPath esa "DOM tez-tez o'zgargani uchun" tavsiya etilmaydi. Yuqoridagi tartib shu tamoyil va Testing Library'ning mashhur ustuvorlik ro'yxatiga asoslangan.

LocatorSaucedemo misoliQachon
getByRolepage.getByRole('button', { name: 'Login' })Tugma, havola, checkbox, sarlavha — deyarli hamma narsa
getByLabelpage.getByLabel('Text input') (Selenium web-form)Label'ga ega forma maydonlari
getByPlaceholderpage.getByPlaceholder('Username')Label yo'q, placeholder bor maydonlar
getByTextpage.getByText('Sauce Labs Backpack')Interaktiv bo'lmagan matnlar
getByAltTextpage.getByAltText('Sauce Labs Backpack')Rasmlar
getByTitlesaucedemo'da title atributli element yo'qtitle atributi bo'yicha (kam ishlatiladi)
getByTestIdpage.getByTestId('shopping-cart-badge')Dasturchilar bilan kelishilgan barqaror kontrakt
CSSpage.locator('[data-test="inventory-item"]')Boshqa yo'l qolmaganda
XPathpage.locator('//input[@data-test="login-button"]')Oxirgi chora

Bu misollarning hammasi testda ishlatib ko'rilgan:

test("login sahifasi: role, placeholder va test id", async ({ page }) => {
  await page.goto("/");
  await page.getByPlaceholder("Username").fill("standard_user");
  await page.getByPlaceholder("Password").fill("secret_sauce");
  await page.getByRole("button", { name: "Login" }).click();

  await expect(page.getByTestId("title")).toHaveText("Products");
  await expect(page.getByText("Sauce Labs Backpack")).toBeVisible();
  await expect(page.getByAltText("Sauce Labs Backpack")).toBeVisible();
  await expect(page.locator('[data-test="inventory-item"]')).toHaveCount(6);
});

💡 Maslahat: Saucedemo kabi saytlarda dasturchilar har bir muhim elementga data-test qo'ygan — bu ular bilan "kontrakt". Shuning uchun kurs testlarida asosan getByTestId ishlatamiz. O'z loyihangizda esa birinchi navbatda getByRoleni sinab ko'ring: u accessibility'ni ham bilvosita tekshiradi (tugmaning "nomi" bo'lmasa, screen reader foydalanuvchisi ham uni tushunmaydi).

Filter va zanjir: aniq elementni topish

Inventory sahifasida 6 ta "Add to cart" tugmasi bor. Bizga aynan Backpack kartasidagisi kerak:

const backpackCard = page
  .getByTestId("inventory-item")
  .filter({ hasText: "Sauce Labs Backpack" });

await backpackCard.getByRole("button", { name: "Add to cart" }).click();

await expect(
  backpackCard.getByRole("button", { name: "Remove" }),
).toBeVisible();
await expect(page.getByTestId("shopping-cart-badge")).toHaveText("1");

filter() variantlari: hasText (matn yoki regex), hasNotText, has (ichida boshqa locator bor), hasNot, visible.

Strict mode — Playwright sizni himoya qiladi

Locator qat'iy: agar u bir nechta elementga mos kelsa va siz unga action qilsangiz, Playwright "omadga" birinchisini bosmaydi — xato beradi. Haqiqiy xabar (1.64.0):

Error: locator.click: Error: strict mode violation: getByRole('button', { name: 'Add to cart' }) resolved to 6 elements:
    1) <button id="add-to-cart-sauce-labs-backpack" ...>Add to cart</button> aka getByTestId('add-to-cart-sauce-labs-backpack')
    2) <button id="add-to-cart-sauce-labs-bike-light" ...>Add to cart</button> aka getByTestId('add-to-cart-sauce-labs-bike-light')
    ...

E'tibor bering: Playwright har bir element uchun tavsiya qilingan locatorni ham ko'rsatadi (aka getByTestId(...)). Tuzatish — locator'ni aniqlashtirish (filter, test id). .first(), .last(), .nth(i) ham strictness'ni o'chiradi, lekin rasmiy hujjat ularni tavsiya qilmaydi: sahifa o'zgarsa, Playwright siz kutmagan elementni bosib yuborishi mumkin.


7. Action'lar {#actions}

ActionMisolIzoh
gotoawait page.goto('/')Sahifani ochish
click / dblclickawait page.getByTestId('checkout').click()Bosish
fillawait page.getByTestId('firstName').fill('Aziz')Maydonni tozalab, matn yozadi
pressSequentiallyawait input.pressSequentially('Aziz')Harfma-harf yozish (har harfga reaksiya qiladigan maydonlar uchun)
pressawait input.press('Enter')Klaviatura tugmasi
check / uncheckawait page.getByLabel('Default checkbox').check()Checkbox va radio
selectOptionawait select.selectOption('lohi')<select>dan qiymat yoki label bo'yicha tanlash
hoverawait item.hover()Sichqonchani olib borish
setInputFilesawait fileInput.setInputFiles('rasm.png')Fayl yuklash
dragToawait card.dragTo(column)Sudrab tashlash

Misol: narx bo'yicha saralash

Saucedemo'dagi saralash <select> qiymatlari: az, za, lohi (Price low to high), hilo.

test("saralash: narx bo'yicha o'sish tartibida", async ({ page }) => {
  await page.goto("/");
  await page.getByTestId("username").fill("standard_user");
  await page.getByTestId("password").fill("secret_sauce");
  await page.getByTestId("login-button").click();

  await page.getByTestId("product-sort-container").selectOption("lohi");
  await expect(page.getByTestId("active-option")).toHaveText(
    "Price (low to high)",
  );

  const priceTexts = await page
    .getByTestId("inventory-item-price")
    .allTextContents();
  const prices = priceTexts.map((text) => Number(text.replace("$", "")));
  const sorted = [...prices].sort((a, b) => a - b);

  expect(prices).toEqual(sorted);
  expect(prices[0]).toBe(7.99);
});

E'tibor bering: avval active-option matni o'zgarganini web-first assertion bilan kutdik, keyin ro'yxatni o'qidik. Agar allTextContents()ni darhol chaqirsak, saralash hali qo'llanmagan eski ro'yxatni o'qib qolishimiz mumkin edi. Narxlarni map bilan songa aylantirish va nusxa olib saralash — QA 17 dagi massiv metodlari.


8. Assertion'lar {#assertions}

Web-first (auto-retrying) assertion'lar

Ular expect(locator) yoki expect(page) bilan yoziladi va shart bajarilguncha qayta tekshiradi (standart 5 soniya):

AssertionNima tekshiradi
toBeVisible() / toBeHidden()Ko'rinadi / ko'rinmaydi (yoki yo'q)
toBeEnabled() / toBeDisabled()Faol / faol emas
toBeChecked()Checkbox yoki radio belgilangan
toHaveText()Matn to'liq mos (massiv berilsa — ro'yxat)
toContainText()Matn ichida bor
toHaveValue()Input qiymati
toHaveCount()Elementlar soni
toHaveAttribute() / toHaveClass()Atribut / CSS class
toBeFocused()Fokusda
toHaveURL() / toHaveTitle()Sahifa manzili / sarlavhasi
toHaveScreenshot()Vizual taqqoslash (QA 19)

Qayta urinmaydigan (generic) assertion'lar

expect(qiymat).toBe(...), toEqual, toContain, toBeGreaterThan — qiymat allaqachon qo'lingizda bo'lganda (masalan, allTextContents() dan keyin). Ular qayta urinmaydi.

// ❌ Beqaror: textContent bir marta o'qiladi, badge hali yangilanmagan bo'lishi mumkin
expect(await page.getByTestId("shopping-cart-badge").textContent()).toBe("2");

// ✅ Barqaror: badge "2" bo'lguncha kutadi
await expect(page.getByTestId("shopping-cart-badge")).toHaveText("2");

Rasmiy hujjat bu haqda ochiq aytadi: sahifalar ma'lumotni asinxron ko'rsatadi, shuning uchun qayta urinmaydigan assertion'lar flaky testga olib keladi — imkon bo'lsa auto-retrying assertion'ni afzal ko'ring.

Soft assertion, izohli xabar va inkor

await test.step("Summalarni tekshirish", async () => {
  await expect
    .soft(page.getByTestId("subtotal-label"))
    .toHaveText("Item total: $39.98");
  await expect.soft(page.getByTestId("tax-label")).toHaveText("Tax: $3.20");
  await expect
    .soft(page.getByTestId("total-label"), "jami summa noto'g'ri")
    .toHaveText("Total: $43.18");
});
  • **expect.soft — yiqilsa ham test davom etadi, oxirida test "failed" deb belgilanadi va barcha** farqlar ro'yxati chiqadi. Bitta sahifadagi bir nechta maydonni tekshirishda qulay.
  • Ikkinchi argument — hisobotda chiqadigan izoh: "jami summa noto'g'ri".
  • **.not** — inkor: await expect(error).not.toBeVisible().

Timeout'lar (rasmiy standart qiymatlar)

TimeoutStandartQanday o'zgartiriladi
Test30 000 msconfig: timeout: 60_000; testda: test.setTimeout(120_000)
Expect5 000 msconfig: expect: { timeout: 10_000 }; joyida: toBeVisible({ timeout: 10_000 })
Action (click, fill)cheklanmagan (test timeout'i doirasida)config: use: { actionTimeout: 10_000 }
Navigatsiya (goto)cheklanmaganconfig: use: { navigationTimeout: 30_000 }

9. Auto-waiting va nega waitForTimeout yomon {#auto-waiting}

Playwright action'dan oldin elementning tayyorligini o'zi tekshiradi (actionability checks). Rasmiy jadvaldan asosiylari:

ActionNimalar kutiladi
click, check, dblclick, uncheckVisible, Stable, Receives Events, Enabled
fill, clearVisible, Enabled, Editable
selectOptionVisible, Enabled
hover, dragToVisible, Stable, Receives Events
  • Visible — elementning o'lchami bor va visibility:hidden emas;
  • Stable — kamida ikki animatsiya kadri davomida joyi o'zgarmagan (animatsiya tugagan);
  • Receives Events — bosiladigan nuqtada aynan shu element turibdi, ustida boshqa oyna (overlay) yo'q;
  • Enabled / Editable — disabled yoki readonly emas.

1-misol: sekin login (performance_glitch_user)

Bu foydalanuvchida "Products" sahifasi ataylab kechikadi. Biz o'lchaganimizda kontent ~5,1 soniyada chiqdi — standart 5 soniyalik expect timeout'idan biroz ko'p. Sleep yozmaymiz, faqat shu assertion'ga ko'proq vaqt beramiz:

test("performance_glitch_user: sekin login, lekin sleep kerak emas", async ({
  page,
}) => {
  await page.goto("/");
  await page.getByTestId("username").fill("performance_glitch_user");
  await page.getByTestId("password").fill("secret_sauce");
  await page.getByTestId("login-button").click();

  // bu foydalanuvchida sahifa ~5 soniyada chiqadi: standart 5s yetmasligi mumkin
  await expect(page.getByTestId("title")).toHaveText("Products", {
    timeout: 15_000,
  });
});

Test sahifa chiqqan zahoti davom etadi: tez bo'lsa — tez, sekin bo'lsa — 15 soniyagacha kutadi. waitForTimeout(15_000) esa har doim 15 soniya yo'qotardi.

🎯 Intervyuda: "Test ba'zan 5 soniyalik timeout bilan yiqiladi. Nima qilasiz?" — Avval sababini aniqlayman: ilova haqiqatan sekinmi (bu performance bug bo'lishi mumkin!) yoki test noto'g'ri narsani kutyaptimi. Agar kechikish kutilgan bo'lsa, faqat o'sha assertion'ga timeout beraman; global timeout'ni ko'tarish yoki sleep qo'shish — muammoni yashirish.

2-misol: kechikib paydo bo'ladigan element

Selenium'ning rasmiy demo sahifasida "Add a box!" tugmasi bosilgandan 1 soniya keyin qizil quti qo'shiladi:

test("dinamik element: 1 soniyadan keyin paydo bo'ladi", async ({ page }) => {
  await page.goto("https://www.selenium.dev/selenium/web/dynamic.html");
  await page.getByRole("button", { name: "Add a box!" }).click();

  // quti ~1 soniyadan keyin DOM'ga qo'shiladi — Playwright o'zi kutadi
  await expect(page.locator(".redbox")).toHaveCount(1);
});

baseURL faqat nisbiy manzillarga qo'shiladi — to'liq URL (https://...) berilsa, o'sha ochiladi.

waitForTimeout o'rniga nima ishlatiladi

Vaziyat❌ Yomon✅ To'g'ri
Element paydo bo'lishini kutishwaitForTimeout(3000)await expect(locator).toBeVisible()
Sahifa almashishini kutishwaitForTimeout(2000)await expect(page).toHaveURL(/inventory/) yoki page.waitForURL(...)
API javobini kutishwaitForTimeout(5000)page.waitForResponse(...) (QA 19)
Element holatiwaitForTimeout(1000)await locator.waitFor({ state: 'hidden' })

⚠️ Diqqat: Mashhur mashq sayti the-internet.herokuapp.comda ham dinamik yuklanish misollari bor. Lekin biz tekshirgan kuni (2026-yil 9-oktabr) uning statik fayllari HTTP 503 qaytardi va sahifa 30 soniyagacha yuklandi — testlar timeout bilan yiqildi. Shuning uchun mashqlar uchun Selenium'ning rasmiy demo sahifalarini tanladik. Bu ham dars: tashqi servisga bog'liq test sizning nazoratingizda bo'lmagan sabablar bilan yiqiladi.


10. Test tuzilmasi: describe, hook'lar va tag'lar {#tuzilma}

Hook'lar

HookQachon ishlaydiTipik ishlatish
test.beforeEachHar testdan oldinSahifani ochish, login
test.afterEachHar testdan keyinTest ma'lumotini tozalash
test.beforeAllWorker'da bir marta, barcha testlardan oldinQimmat tayyorgarlik (ehtiyot bo'ling: holat testlar orasida bo'linadi)
test.afterAllWorker'da bir marta, oxiridaUmumiy tozalash

Annotatsiyalar

AnnotatsiyaMa'nosi (rasmiy hujjat bo'yicha)
test.skip()Test ishga tushirilmaydi
test.fail()Test ishga tushiriladi va yiqilishi kutiladi; agar o'tib ketsa — Playwright xabar beradi
test.fixme()Test yiqiladi deb belgilanadi va ishga tushirilmaydi
test.slow()Test timeout'ini 3 barobar oshiradi
test.only()Faqat shu test(lar) ishlaydi — CI'da forbidOnly bilan taqiqlang

Tag'lar

test(
  "standard_user muvaffaqiyatli kiradi",
  { tag: "@smoke" },
  async ({ page }) => {
    // ...
  },
);

test("to'liq hisobot", { tag: ["@slow", "@regression"] }, async ({ page }) => {
  // ...
});

Tag @ bilan boshlanishi shart. Ishga tushirish:

npx playwright test --grep @smoke              # faqat @smoke
npx playwright test --grep-invert @slow        # @slow'dan tashqari hammasi
npx playwright test --grep "@smoke|@critical"  # YOKI

Bizning loyihada --grep @smoke --list natijasi (1.64.0):

Listing tests:
  [chromium] › ui/cart.spec.ts:12:7 › Savatcha › 2 ta mahsulot qo'shilsa badge 2 ni ko'rsatadi
  [chromium] › ui/checkout.spec.ts:12:7 › Checkout › to'liq buyurtma: savatchadan 'Thank you' sahifasigacha
  [chromium] › ui/login.spec.ts:8:7 › Login › standard_user muvaffaqiyatli kiradi
Total: 3 tests in 3 files

💡 Maslahat: Har PR'da tez @smoke to'plami, kechasi esa to'liq regression — bu strategiyani QA 21 da CI'ga ulaymiz.

test.step — hisobotni o'qiladigan qilish

await test.step("Savatchaga 2 ta mahsulot qo'shish", async () => {
  await page.getByTestId("add-to-cart-sauce-labs-backpack").click();
  await page.getByTestId("add-to-cart-sauce-labs-bike-light").click();
});

HTML hisobot va trace'da qadamlar shu nomlar bilan ko'rinadi — yiqilganda qaysi bosqichda ekani darhol tushunarli.


11. To'liq misol: savatcha va checkout {#savatcha-checkout}

tests/ui/cart.spec.ts:

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

test.describe("Savatcha", () => {
  test.beforeEach(async ({ page }) => {
    await page.goto("/");
    await page.getByTestId("username").fill("standard_user");
    await page.getByTestId("password").fill("secret_sauce");
    await page.getByTestId("login-button").click();
    await expect(page.getByTestId("title")).toHaveText("Products");
  });

  test(
    "2 ta mahsulot qo'shilsa badge 2 ni ko'rsatadi",
    { tag: "@smoke" },
    async ({ page }) => {
      await page.getByTestId("add-to-cart-sauce-labs-backpack").click();
      await page.getByTestId("add-to-cart-sauce-labs-bike-light").click();

      await expect(page.getByTestId("shopping-cart-badge")).toHaveText("2");
    },
  );

  test("savatcha sahifasida qo'shilgan mahsulotlar ko'rinadi", async ({
    page,
  }) => {
    await page.getByTestId("add-to-cart-sauce-labs-backpack").click();
    await page.getByTestId("add-to-cart-sauce-labs-bike-light").click();
    await page.getByTestId("shopping-cart-link").click();

    await expect(page).toHaveURL(/\/cart\.html$/);
    await expect(page.getByTestId("title")).toHaveText("Your Cart");
    await expect(page.getByTestId("inventory-item-name")).toHaveText([
      "Sauce Labs Backpack",
      "Sauce Labs Bike Light",
    ]);
  });

  test("mahsulot olib tashlansa badge kamayadi va oxirida yo'qoladi", async ({
    page,
  }) => {
    await page.getByTestId("add-to-cart-sauce-labs-backpack").click();
    await page.getByTestId("add-to-cart-sauce-labs-bike-light").click();
    await page.getByTestId("remove-sauce-labs-backpack").click();

    await expect(page.getByTestId("shopping-cart-badge")).toHaveText("1");

    await page.getByTestId("remove-sauce-labs-bike-light").click();
    await expect(page.getByTestId("shopping-cart-badge")).toBeHidden();
  });
});

toHaveText([...]) massiv bilan — ro'yxatdagi barcha elementlar matnini tartibi bilan tekshiradi. toBeHidden() — element yo'q bo'lsa ham o'tadi (bo'sh savatchada badge umuman chizilmaydi).

tests/ui/checkout.spec.ts:

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

test.describe("Checkout", () => {
  test.beforeEach(async ({ page }) => {
    await page.goto("/");
    await page.getByTestId("username").fill("standard_user");
    await page.getByTestId("password").fill("secret_sauce");
    await page.getByTestId("login-button").click();
    await expect(page.getByTestId("title")).toHaveText("Products");
  });

  test(
    "to'liq buyurtma: savatchadan 'Thank you' sahifasigacha",
    { tag: "@smoke" },
    async ({ page }) => {
      await page.getByTestId("add-to-cart-sauce-labs-backpack").click();
      await page.getByTestId("add-to-cart-sauce-labs-bike-light").click();
      await page.getByTestId("shopping-cart-link").click();
      await page.getByTestId("checkout").click();

      await page.getByTestId("firstName").fill("Aziz");
      await page.getByTestId("lastName").fill("Karimov");
      await page.getByTestId("postalCode").fill("100000");
      await page.getByTestId("continue").click();

      await expect(page.getByTestId("title")).toHaveText("Checkout: Overview");
      await expect(page.getByTestId("subtotal-label")).toHaveText(
        "Item total: $39.98",
      );
      await expect(page.getByTestId("tax-label")).toHaveText("Tax: $3.20");
      await expect(page.getByTestId("total-label")).toHaveText("Total: $43.18");

      await page.getByTestId("finish").click();

      await expect(page).toHaveURL(/\/checkout-complete\.html$/);
      await expect(page.getByTestId("complete-header")).toHaveText(
        "Thank you for your order!",
      );
    },
  );

  test("bo'sh forma: First Name majburiy", async ({ page }) => {
    await page.getByTestId("shopping-cart-link").click();
    await page.getByTestId("checkout").click();
    await page.getByTestId("continue").click();

    await expect(page.getByTestId("error")).toHaveText(
      "Error: First Name is required",
    );
  });
});

Uchala fayl birga (Chromium):

Running 9 tests using 4 workers
  ...
  9 passed (4.8s)

Bu kodda nima yoqmaydi?

Ishlaydi, lekin uchta muammo ko'zga tashlanadi:

  1. Login 3 joyda takrorlangan. Login sahifasi o'zgarsa — 3 faylni tuzatasiz.
  2. Locator'lar testlar ichida sochilgan. getByTestId('shopping-cart-link') ko'p joyda.
  3. Login ma'lumotlari kod ichida. standard_user / secret_sauce testlarga qattiq yozilgan.

Keyingi darsda (QA 19) aynan shu uch faylni Page Object Modelga o'tkazamiz: LoginPage (login(), errorMessage), InventoryPage (addToCart(name), sortBy('lohi'), prices()), Header (cartBadge, openCart()), CartPage va CheckoutPage; login'ni storageState bilan bir marta bajaramiz, foydalanuvchi ma'lumotlarini esa .envdagi SAUCE_USERNAME / SAUCE_PASSWORDga ko'chiramiz.


12. Debugging: trace, UI mode va Inspector {#debugging}

Yiqilgan test nima deydi

Kutilgan matnda bitta harf xato bo'lsa ('Product' o'rniga 'Products' kerak edi), Playwright 1.64 shunday yozadi:

Error: expect(locator).toHaveText(expected) failed

Locator:  getByTestId('title')
Expected: "Product"
Received: "Products"
Timeout:  5000ms

Call log:
  - Expect "toHaveText" getByTestId('title') with timeout 5000ms
  - waiting for getByTestId('title')
    13 × locator resolved to <span class="title" data-test="title">Products</span>
       - unexpected value "Products"

Xabarni o'qishni o'rganing: Locator (nimani qidirdik), Expected/Received (nima kutdik va nima keldi), Call log (Playwright 5 soniya davomida 13 marta tekshirdi). Bundan tashqari test-results/ papkasida screenshot, video va error-context.md fayli paydo bo'ladi — unda xato va sahifaning ARIA tuzilmasi (snapshot) bor; uni hamkasbingizga yoki AI yordamchiga berish mumkin (QA 26).

Asboblar

AsbobQanday ochiladiQachon
UI modenpx playwright test --uiKundalik ish: testlarni tanlash, watch rejimi, har qadamning "vaqt sayohati"
Inspectornpx playwright test --debug yoki kodda await page.pause()Qadamma-qadam yurish, locator'ni jonli sinash
Trace viewernpx playwright show-trace path/to/trace.zip yoki HTML hisobotdagi trace belgisiCI'da yiqilgan testni tahlil qilish
HTML reportnpx playwright show-reportBarcha natijalar, screenshot, video, trace
VS Code kengaytmasiTesting paneliTestni muharrirdan ishga tushirish, Record new, Pick locator

Trace viewer nimalarni ko'rsatadi (rasmiy hujjat): har bir action va unda ishlatilgan locator, har qadamdagi to'liq DOM snapshot, screencast "film lentasi", tarmoq so'rovlari, brauzer console'i, xatolar va manba kodi. Trace'ni CLI'dan majburan yoqish: npx playwright test --trace on.

💡 Maslahat: CI'da video emas, trace ishlating — Playwright'ning best practices sahifasi aynan shuni tavsiya qiladi. Trace'ni trace.playwright.dev da ham ochish mumkin; rasmiy hujjatga ko'ra u faylni to'liq brauzeringizda yuklaydi va hech qayerga yubormaydi.


13. Amaliy mashqlar {#mashqlar}

Barcha javoblar Playwright 1.64.0 + Chromium'da ishga tushirib tekshirilgan. Saucedemo mashqlari uchun yuqoridagi config (baseURL, testIdAttribute: 'data-test') ishlatiladi.

Mashq 1: Xato xabarini yopish

standard_user + wrong_password bilan kirishga urining. Xato matnini tekshiring, keyin xato yonidagi "×" tugmasini (data-test="error-button") bosib, xato yo'qolganini tasdiqlang.

<details> <summary>💡 Javobni ko'rish</summary>

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

test("noto'g'ri parol va xatoni yopish", async ({ page }) => {
  await page.goto("/");
  await page.getByTestId("username").fill("standard_user");
  await page.getByTestId("password").fill("wrong_password");
  await page.getByTestId("login-button").click();

  const error = page.getByTestId("error");
  await expect(error).toHaveText(
    "Epic sadface: Username and password do not match any user in this service",
  );

  await page.getByTestId("error-button").click();
  await expect(error).toBeHidden();
});

error locator'ini bir marta yaratib, ikki marta ishlatdik — locator "dangasa": har safar qaytadan qidiradi.

</details>

Mashq 2: Saralash

Login qiling va ikkita test yozing: (a) "Name (Z to A)" tanlanganda birinchi mahsulot Test.allTheThings() T-Shirt (Red); (b) "Price (high to low)" tanlanganda birinchi narx $49.99, oxirgisi $7.99.

<details> <summary>💡 Javobni ko'rish</summary>

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

test.describe("Saralash", () => {
  test.beforeEach(async ({ page }) => {
    await page.goto("/");
    await page.getByTestId("username").fill("standard_user");
    await page.getByTestId("password").fill("secret_sauce");
    await page.getByTestId("login-button").click();
  });

  test("Name (Z to A): birinchi mahsulot", async ({ page }) => {
    await page.getByTestId("product-sort-container").selectOption("za");
    await expect(page.getByTestId("inventory-item-name").first()).toHaveText(
      "Test.allTheThings() T-Shirt (Red)",
    );
  });

  test("Price (high to low): eng qimmati birinchi", async ({ page }) => {
    await page.getByTestId("product-sort-container").selectOption("hilo");
    await expect(page.getByTestId("inventory-item-price").first()).toHaveText(
      "$49.99",
    );
    await expect(page.getByTestId("inventory-item-price").last()).toHaveText(
      "$7.99",
    );
  });
});

Bu yerda .first() va .last() o'rinli: biz ataylab "ro'yxatning birinchisi" haqida gapiryapmiz — tartibning o'zi tekshiruv mavzusi.

</details>

Mashq 3: TodoMVC

demo.playwright.dev/todomvc da: 3 ta vazifa qo'shing ("Choy damlash", "Kitob o'qish", "Testlarni yozish"), birinchisini bajarilgan deb belgilang, hisoblagich 2 items left ekanini, "Active" filtrida 2 ta, "Completed"da 1 ta vazifa qolganini tekshiring. Diqqat: bu sayt data-test emas, **data-testid** ishlatadi.

<details> <summary>💡 Javobni ko'rish</summary>

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

// TodoMVC demo data-testid ishlatadi, shu fayl uchun atributni almashtiramiz
test.use({ testIdAttribute: "data-testid" });

test("TodoMVC — qo'shish, bajarish va filtrlash", async ({ page }) => {
  await page.goto("https://demo.playwright.dev/todomvc/");

  const newTodo = page.getByPlaceholder("What needs to be done?");
  for (const title of ["Choy damlash", "Kitob o'qish", "Testlarni yozish"]) {
    await newTodo.fill(title);
    await newTodo.press("Enter");
  }

  await expect(page.getByTestId("todo-title")).toHaveCount(3);

  await page
    .getByTestId("todo-item")
    .filter({ hasText: "Choy damlash" })
    .getByRole("checkbox", { name: "Toggle Todo" })
    .check();

  await expect(page.getByTestId("todo-count")).toHaveText("2 items left");

  await page.getByRole("link", { name: "Active" }).click();
  await expect(page.getByTestId("todo-title")).toHaveText([
    "Kitob o'qish",
    "Testlarni yozish",
  ]);

  await page.getByRole("link", { name: "Completed" }).click();
  await expect(page.getByTestId("todo-title")).toHaveText(["Choy damlash"]);
});

test.use({ testIdAttribute: 'data-testid' }) — config'dagi sozlamani faqat shu fayl uchun almashtiradi. Qiziq fakt: tekshiruv paytida biz ro'yxatni oddiy (qayta urinmaydigan) usulda o'qiganimizda, filtr bosilgandan keyin hali eski ro'yxat qaytgan edi. toHaveText([...]) esa ro'yxat yangilanguncha kutdi — web-first assertion'ning qiymati aynan shunda.

</details>

Mashq 4: Veb-forma (getByLabel)

Selenium'ning rasmiy demo formasida: "Text input"ga ism, "Password"ga parol yozing; "Dropdown (select)"dan Two (qiymati 2) ni tanlang; "Checked checkbox"ni o'chiring, "Default checkbox"ni yoqing, "Default radio"ni tanlang; "Disabled input" faol emasligini tekshiring. "Submit"dan keyin Form submitted sarlavhasi va Received! matni chiqishi kerak.

<details> <summary>💡 Javobni ko'rish</summary>

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

test("veb-forma — label orqali to'ldirish", async ({ page }) => {
  await page.goto("https://www.selenium.dev/selenium/web/web-form.html");

  await page.getByLabel("Text input").fill("Aziz Karimov");
  await page.getByLabel("Password").fill("Parol123");
  await page.getByLabel("Dropdown (select)").selectOption("2");
  await page.getByLabel("Checked checkbox").uncheck();
  await page.getByLabel("Default checkbox").check();
  await page.getByLabel("Default radio").check();

  await expect(page.getByLabel("Dropdown (select)")).toHaveValue("2");
  await expect(page.getByLabel("Checked checkbox")).not.toBeChecked();
  await expect(page.getByLabel("Default checkbox")).toBeChecked();
  await expect(page.getByLabel("Checked radio")).not.toBeChecked();
  await expect(page.getByLabel("Disabled input")).toBeDisabled();

  await page.getByRole("button", { name: "Submit" }).click();

  await expect(
    page.getByRole("heading", { name: "Form submitted" }),
  ).toBeVisible();
  await expect(page.getByText("Received!")).toBeVisible();
});

Label'li formada getByLabel eng tabiiy tanlov: foydalanuvchi ham maydonni label'i bo'yicha taniydi. Radio guruhda bittasini tanlash ikkinchisini avtomatik o'chiradi — buni not.toBeChecked() bilan tasdiqladik.

</details>

Mashq 5: Dinamik elementlar — sleep'siz

dynamic.html sahifasida: "Add a box!"ni ikki marta bosing va 2 ta qizil quti (.redbox) paydo bo'lishini kuting. Keyin "Reveal a new input" bosib, yashirin input (#revealed) ko'ringanini tekshiring va unga matn yozing. Hech qayerda waitForTimeout ishlatmang.

<details> <summary>💡 Javobni ko'rish</summary>

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

test("dinamik elementlar — sleep yozmasdan", async ({ page }) => {
  await page.goto("https://www.selenium.dev/selenium/web/dynamic.html");

  await page.getByRole("button", { name: "Add a box!" }).click();
  await page.getByRole("button", { name: "Add a box!" }).click();
  await expect(page.locator(".redbox")).toHaveCount(2);

  await page.getByRole("button", { name: "Reveal a new input" }).click();
  const revealed = page.locator("#revealed");
  await expect(revealed).toBeVisible();
  await revealed.fill("Paydo bo'ldim!");
  await expect(revealed).toHaveValue("Paydo bo'ldim!");
});

Qutilarda na role, na test id bor — shuning uchun CSS (.redbox, #revealed) ishlatildi. Bu "oxirgi chora" holati: real loyihada dasturchilardan test id qo'shishni so'ragan bo'lardik.

</details>

Mashq 6: Strict mode xatosini tuzating

Quyidagi kod strict mode violation ... resolved to 6 elements xatosini beradi. Uni shunday tuzatingki, aynan Sauce Labs Bike Light savatchaga qo'shilsin.

await page.getByRole("button", { name: "Add to cart" }).click();

<details> <summary>💡 Javobni ko'rish</summary>

await page
  .getByTestId("inventory-item")
  .filter({ hasText: "Sauce Labs Bike Light" })
  .getByRole("button", { name: "Add to cart" })
  .click();

await expect(page.getByTestId("shopping-cart-badge")).toHaveText("1");

Yoki qisqaroq — xato xabarining o'zi taklif qilgan locator: page.getByTestId('add-to-cart-sauce-labs-bike-light'). .nth(1) ham "ishlaydi", lekin mahsulotlar tartibi o'zgarsa, jimgina boshqa mahsulotni qo'shadi — bu yashirin bug.

</details>

Mashq 7: Bug ovi — problem_user

problem_user bilan kirganda mahsulot rasmlarida nimadir noto'g'ri. Barcha 6 ta mahsulot rasmining src atributini yig'ib, ularning hammasi har xil bo'lishi kerakligini tekshiradigan test yozing. Bug ma'lum bo'lgani uchun testni test.fail() bilan belgilang.

<details> <summary>💡 Javobni ko'rish</summary>

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

test("problem_user rasmlar bug'ini topish", async ({ page }) => {
  test.fail(
    true,
    "problem_user'da barcha mahsulot rasmlari bir xil (ma'lum bug)",
  );

  await page.goto("/");
  await page.getByTestId("username").fill("problem_user");
  await page.getByTestId("password").fill("secret_sauce");
  await page.getByTestId("login-button").click();
  await expect(page.getByTestId("inventory-item")).toHaveCount(6);

  const sources = await page
    .locator(".inventory_item img")
    .evaluateAll((images) => images.map((img) => img.getAttribute("src")));
  const unique = new Set(sources);

  expect(unique.size, "har bir mahsulotning o'z rasmi bo'lishi kerak").toBe(6);
});

Biz tekshirganda problem_userda 6 ta rasmning hammasi bir xil fayl (/assets/sl-404-....jpg) edi, standard_userda esa 6 xil. list reporter bu testni ✘ belgisi bilan ko'rsatadi, lekin umumiy natija passed — test kutilganidek yiqildi. Bug tuzatilgach test "kutilmaganda o'tdi" deb xabar beradi — shunda test.fail()ni olib tashlash vaqti keldi.

</details>

Mashq 8: Tag'lar bilan ishlash

Bizning uchta UI faylda @smoke tegi qaysi testlarda bor? Quyidagi buyruqlar nechta testni ishga tushiradi: (a) --grep @smoke, (b) --grep-invert @smoke, (c) -g "LOCKED"? Javobni --list bilan tekshiring.

<details> <summary>💡 Javobni ko'rish</summary>

  • (a) 3 ta: login (standard_user muvaffaqiyatli kiradi), cart (2 ta mahsulot qo'shilsa badge 2 ni ko'rsatadi), checkout (to'liq buyurtma).
  • (b) 6 ta: qolgan barcha testlar (jami 9 − 3).
  • (c) 1 ta: locked_out_user bloklangani haqida xabar oladi — --grep katta-kichik harfni farqlamaydi.
npx playwright test tests/ui --project=chromium --grep @smoke --list
npx playwright test tests/ui --project=chromium --grep-invert @smoke --list
npx playwright test tests/ui --project=chromium -g "LOCKED" --list

</details>


14. O'zingizni tekshiring {#checklist}

  • [ ] Playwright'ni ikki usulda o'rnata olaman va brauzerlarni yangilashni bilaman
  • [ ] playwright.config.tsdagi baseURL, testIdAttribute, retries, trace, projects nima qilishini tushuntira olaman
  • [ ] Locator tanlash tartibini va strict mode xatosini tuzatishni bilaman
  • [ ] Web-first va qayta urinmaydigan assertion farqini tushuntira olaman
  • [ ] Actionability tekshiruvlarini va waitForTimeout o'rniga nima ishlatishni bilaman
  • [ ] Hook, annotatsiya va tag'lar bilan testlarni tashkil qila olaman
  • [ ] Yiqilgan testni xato xabari, trace va UI mode orqali tahlil qila olaman

Xulosa {#xulosa}

  • Playwright har test uchun yangi BrowserContext beradi — testlar izolyatsiyalangan va parallel ishlaydi.
  • O'rnatish: npm init playwright@latest (yoki qo'lda @playwright/test + npx playwright install); Playwright'ni yangilaganda brauzerlarni ham yangilang.
  • baseURL va testIdAttribute (saucedemo uchun data-test) testlarni qisqa va muhitga bog'liq bo'lmagan qiladi.
  • Locator tartibi: getByRole → label/placeholder/matn → test id → CSS → XPath. Strict mode xatosi — locator'ni aniqlashtirish signali, .nth() bilan "o'chirish" emas.
  • Web-first assertion'lar (await expect(locator)...) kutadi va qayta urinadi; textContent() + toBe() — flaky test retsepti.
  • Auto-waiting actionability'ni o'zi tekshiradi; waitForTimeout o'rniga assertion, waitForURL, waitForResponse ishlating; kutilgan sekinlik uchun faqat o'sha assertion'ga timeout bering.
  • Debugging: xato xabarini o'qing (Locator / Expected / Received / Call log), keyin UI mode, Inspector va trace viewer.
  • Keyingi dars: QA 19: Playwright (2) — Page Object Model, fixtures, auth state, network mocking va visual testlar

Manbalar {#manbalar}

  1. Playwright — Installation
  2. Playwright — Writing tests
  3. Playwright — Running and debugging tests
  4. Playwright — Test configuration
  5. Playwright — Locators
  6. Playwright — Actions (input)
  7. Playwright — Assertions
  8. Playwright — Auto-waiting (actionability)
  9. Playwright — Timeouts
  10. Playwright — Annotations va tag'lar
  11. Playwright — Test generator (codegen)
  12. Playwright — UI mode
  13. Playwright — Debugging tests
  14. Playwright — Trace viewer
  15. Playwright — Isolation va Browser Contexts
  16. Playwright — Getting started with VS Code
  17. Testing Library — Query priority
  18. Sauce Labs demo do'koni — saucedemo.com
  19. Selenium demo sahifalari — web form

Markdown manba

iOS Lessons