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
waitForTimeoutyomonligini tushunasiz; - yiqilgan testni trace viewer, UI mode va Inspector bilan tahlil qilasiz.
Mundarija
- Playwright nima va qanday ishlaydi
- O'rnatish
- playwright.config.ts asoslari
- Testlarni ishga tushirish va codegen
- Birinchi test: saucedemo login
- Locator'lar
- Action'lar
- Assertion'lar
- Auto-waiting va nega waitForTimeout yomon
- Test tuzilmasi: describe, hook'lar va tag'lar
- To'liq misol: savatcha va checkout
- Debugging: trace, UI mode va Inspector
- Amaliy mashqlar
- O'zingizni tekshiring
- Xulosa
- 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:
| Savol | Standart javob | Bizning tanlov |
|---|---|---|
| TypeScript yoki JavaScript | TypeScript | TypeScript |
| Testlar papkasi nomi | tests (agar tests bor bo'lsa — e2e) | tests |
| GitHub Actions workflow qo'shilsinmi | — | Ha (QA 21 da kerak bo'ladi) |
| Playwright brauzerlari o'rnatilsinmi | Ha | Ha |
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, keyinnpx playwright install --with-deps. Linux CI serverlarda--with-depsbrauzer 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"] } },
],
});
| Sozlama | Ma'nosi |
|---|---|
testDir | Testlar qayerdan qidiriladi |
fullyParallel | Bir fayl ichidagi testlar ham parallel ishlaydi |
forbidOnly | CI'da test.only qolib ketsa — yiqitadi (hamma testlar o'tkazib yuborilmasin) |
retries | Yiqilgan testni qayta urinish soni: lokalda 0, CI'da 2 |
workers | Parallel jarayonlar soni (undefined — standart, CPU yadrolarining 50%) |
reporter | Hisobot turi: list, html, junit... |
use.baseURL | page.goto('/') shu manzilga qo'shiladi — muhit almashtirish oson |
use.testIdAttribute | getByTestId() qaysi atributni qidirishi. Standart — data-testid, saucedemo esa data-test ishlatadi |
use.trace | on-first-retry — trace faqat birinchi qayta urinishda yoziladi (rasmiy CI tavsiyasi) |
use.screenshot / use.video | Yiqilgan testning screenshot va videosini saqlaydi |
projects | Bir 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}
| Buyruq | Nima qiladi |
|---|---|
npx playwright test | Barcha testlar, barcha project'lar |
npx playwright test tests/ui/login.spec.ts | Bitta fayl |
npx playwright test tests/ui/login.spec.ts:8 | Faqat 8-qatordagi test |
npx playwright test -g "locked" | Nomida "locked" bor testlar (katta-kichik harf farqlanmaydi) |
npx playwright test --project=chromium | Faqat bitta project |
npx playwright test --headed | Brauzer oynasini ko'rsatib |
npx playwright test --ui | Interaktiv UI mode |
npx playwright test --debug | Playwright Inspector bilan qadamma-qadam |
npx playwright test --workers=1 | Ketma-ket (parallelsiz) |
npx playwright test --repeat-each=10 | Har testni 10 marta — flaky testni ovlash uchun |
npx playwright test --last-failed | Faqat oldingi safar yiqilganlar |
npx playwright test --list | Testlarni ishga tushirmay, ro'yxatini ko'rsatadi |
npx playwright test -x | Birinchi yiqilishda to'xtaydi |
npx playwright show-report | HTML 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('/')—baseURLbilan birlashibhttps://www.saucedemo.com/ga boradi.getByTestId('username')—data-test="username"atributli element (config'dagitestIdAttributetufayli).await expect(...).toHaveText(...)— matn kutilganiga teng bo'lguncha (standart 5 soniyagacha) qayta tekshiradi.toHaveURL('/')— nisbiy URL hambaseURLbo'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.
| Locator | Saucedemo misoli | Qachon |
|---|---|---|
getByRole | page.getByRole('button', { name: 'Login' }) | Tugma, havola, checkbox, sarlavha — deyarli hamma narsa |
getByLabel | page.getByLabel('Text input') (Selenium web-form) | Label'ga ega forma maydonlari |
getByPlaceholder | page.getByPlaceholder('Username') | Label yo'q, placeholder bor maydonlar |
getByText | page.getByText('Sauce Labs Backpack') | Interaktiv bo'lmagan matnlar |
getByAltText | page.getByAltText('Sauce Labs Backpack') | Rasmlar |
getByTitle | saucedemo'da title atributli element yo'q | title atributi bo'yicha (kam ishlatiladi) |
getByTestId | page.getByTestId('shopping-cart-badge') | Dasturchilar bilan kelishilgan barqaror kontrakt |
| CSS | page.locator('[data-test="inventory-item"]') | Boshqa yo'l qolmaganda |
| XPath | page.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-testqo'ygan — bu ular bilan "kontrakt". Shuning uchun kurs testlarida asosangetByTestIdishlatamiz. O'z loyihangizda esa birinchi navbatdagetByRoleni 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}
| Action | Misol | Izoh |
|---|---|---|
goto | await page.goto('/') | Sahifani ochish |
click / dblclick | await page.getByTestId('checkout').click() | Bosish |
fill | await page.getByTestId('firstName').fill('Aziz') | Maydonni tozalab, matn yozadi |
pressSequentially | await input.pressSequentially('Aziz') | Harfma-harf yozish (har harfga reaksiya qiladigan maydonlar uchun) |
press | await input.press('Enter') | Klaviatura tugmasi |
check / uncheck | await page.getByLabel('Default checkbox').check() | Checkbox va radio |
selectOption | await select.selectOption('lohi') | <select>dan qiymat yoki label bo'yicha tanlash |
hover | await item.hover() | Sichqonchani olib borish |
setInputFiles | await fileInput.setInputFiles('rasm.png') | Fayl yuklash |
dragTo | await 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):
| Assertion | Nima 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)
| Timeout | Standart | Qanday o'zgartiriladi |
|---|---|---|
| Test | 30 000 ms | config: timeout: 60_000; testda: test.setTimeout(120_000) |
| Expect | 5 000 ms | config: expect: { timeout: 10_000 }; joyida: toBeVisible({ timeout: 10_000 }) |
Action (click, fill) | cheklanmagan (test timeout'i doirasida) | config: use: { actionTimeout: 10_000 } |
Navigatsiya (goto) | cheklanmagan | config: 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:
| Action | Nimalar kutiladi |
|---|---|
click, check, dblclick, uncheck | Visible, Stable, Receives Events, Enabled |
fill, clear | Visible, Enabled, Editable |
selectOption | Visible, Enabled |
hover, dragTo | Visible, Stable, Receives Events |
- Visible — elementning o'lchami bor va
visibility:hiddenemas; - 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 —
disabledyokireadonlyemas.
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
sleepqo'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 kutish | waitForTimeout(3000) | await expect(locator).toBeVisible() |
| Sahifa almashishini kutish | waitForTimeout(2000) | await expect(page).toHaveURL(/inventory/) yoki page.waitForURL(...) |
| API javobini kutish | waitForTimeout(5000) | page.waitForResponse(...) (QA 19) |
| Element holati | waitForTimeout(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
| Hook | Qachon ishlaydi | Tipik ishlatish |
|---|---|---|
test.beforeEach | Har testdan oldin | Sahifani ochish, login |
test.afterEach | Har testdan keyin | Test ma'lumotini tozalash |
test.beforeAll | Worker'da bir marta, barcha testlardan oldin | Qimmat tayyorgarlik (ehtiyot bo'ling: holat testlar orasida bo'linadi) |
test.afterAll | Worker'da bir marta, oxirida | Umumiy tozalash |
Annotatsiyalar
| Annotatsiya | Ma'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
@smoketo'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:
- Login 3 joyda takrorlangan. Login sahifasi o'zgarsa — 3 faylni tuzatasiz.
- Locator'lar testlar ichida sochilgan.
getByTestId('shopping-cart-link')ko'p joyda. - Login ma'lumotlari kod ichida.
standard_user/secret_saucetestlarga 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
| Asbob | Qanday ochiladi | Qachon |
|---|---|---|
| UI mode | npx playwright test --ui | Kundalik ish: testlarni tanlash, watch rejimi, har qadamning "vaqt sayohati" |
| Inspector | npx playwright test --debug yoki kodda await page.pause() | Qadamma-qadam yurish, locator'ni jonli sinash |
| Trace viewer | npx playwright show-trace path/to/trace.zip yoki HTML hisobotdagi trace belgisi | CI'da yiqilgan testni tahlil qilish |
| HTML report | npx playwright show-report | Barcha natijalar, screenshot, video, trace |
| VS Code kengaytmasi | Testing paneli | Testni 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—--grepkatta-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.tsdagibaseURL,testIdAttribute,retries,trace,projectsnima qilishini tushuntira olaman - [ ] Locator tanlash tartibini va strict mode xatosini tuzatishni bilaman
- [ ] Web-first va qayta urinmaydigan assertion farqini tushuntira olaman
- [ ] Actionability tekshiruvlarini va
waitForTimeouto'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
BrowserContextberadi — 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. baseURLvatestIdAttribute(saucedemo uchundata-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;
waitForTimeouto'rniga assertion,waitForURL,waitForResponseishlating; 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}
- Playwright — Installation
- Playwright — Writing tests
- Playwright — Running and debugging tests
- Playwright — Test configuration
- Playwright — Locators
- Playwright — Actions (input)
- Playwright — Assertions
- Playwright — Auto-waiting (actionability)
- Playwright — Timeouts
- Playwright — Annotations va tag'lar
- Playwright — Test generator (codegen)
- Playwright — UI mode
- Playwright — Debugging tests
- Playwright — Trace viewer
- Playwright — Isolation va Browser Contexts
- Playwright — Getting started with VS Code
- Testing Library — Query priority
- Sauce Labs demo do'koni — saucedemo.com
- Selenium demo sahifalari — web form