Playwright — End-to-End testing, brauzerda test

~25 daq · Next.js · Testing

Muammodan boshlaylik

Unit testlar yashil. Komponent testlari yashil. Deploy qildingiz — va "Buyurtma berish" ishlamayapti: middleware login sahifasiga yo'naltiradi, Server Action cookie ni ko'rmaydi, chunki production da secure cookie sozlamasi boshqacha.

Har bir qism alohida ishlaydi — lekin birgalikda emas. Bunday xatolarni faqat haqiqiy brauzerda, haqiqiy server bilan, foydalanuvchi kabi harakat qilib topish mumkin.


E2E nima?

End-to-End test — ilovani boshidan oxirigacha, haqiqiy foydalanuvchi kabi sinash: brauzer ochiladi, sahifaga kiriladi, tugmalar bosiladi, formalar to'ldiriladi.

Playwright — Microsoft ning E2E vositasi:

  • Chromium, Firefox, WebKit (Safari) — uchala dvigatel
  • Avtomatik kutish — element paydo bo'lishini o'zi kutadi
  • Parallel ishga tushirish, trace va video yozish
  • Mobil qurilmalar emulyatsiyasi

Hayotiy o'xshatma

Yangi restoranni ochishdan oldin "sinov kechasi": do'stlar haqiqiy mijozlardek kelib, stol band qiladi, buyurtma beradi, ovqatlanadi va hisob to'laydi. Oshxona, zal va kassa alohida sinalgan — lekin faqat shu kechada ular birga ishlay olishi aniq bo'ladi.


O'rnatish

npm init playwright@latest

Savollar: TypeScript, testlar papkasi (e2e), GitHub Actions workflow, brauzerlarni o'rnatish.

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
  testDir: './e2e',
  fullyParallel: true,
  retries: process.env.CI ? 2 : 0,
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'mobil', use: { ...devices['Pixel 7'] } },
  ],
  webServer: {
    command: 'npm run build && npm run start',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
    timeout: 120_000,
  },
})

webServer — testlardan oldin Next.js ni production rejimida ishga tushiradi (development emas — production xatti-harakatini sinash uchun).


Birinchi test

// e2e/katalog.spec.ts
import { test, expect } from '@playwright/test'

test('katalogdan mahsulot sahifasiga o\'tish', async ({ page }) => {
  await page.goto('/katalog')

  await expect(page.getByRole('heading', { name: 'Katalog' })).toBeVisible()

  await page.getByRole('link', { name: /Choynak/ }).first().click()

  await expect(page).toHaveURL(/\/mahsulot\//)
  await expect(page.getByRole('button', { name: 'Savatga' })).toBeVisible()
})

test('filtr URL ni o\'zgartiradi', async ({ page }) => {
  await page.goto('/katalog')
  await page.getByLabel('Kategoriya').selectOption('telefon')
  await expect(page).toHaveURL(/kategoriya=telefon/)
})
npx playwright test             # hammasi
npx playwright test --ui        # interaktiv UI rejim
npx playwright test --headed    # brauzerni ko'rib turish
npx playwright show-report      # HTML hisobot

Lokatorlar — RTL dagi kabi (62-dars): getByRole, getByLabel, getByText. expect lar — avtomatik kutadi (standart 5 s).


To'liq oqim — buyurtma berish

test('mehmon savatga qo\'shib, kirib, buyurtma beradi', async ({ page }) => {
  await page.goto('/mahsulot/choynak')
  await page.getByRole('button', { name: 'Savatga' }).click()
  await expect(page.getByTestId('savat-soni')).toHaveText('1')

  await page.getByRole('link', { name: 'Savat' }).click()
  await page.getByRole('button', { name: 'Rasmiylashtirish' }).click()

  // login ga yo'naltirildi
  await expect(page).toHaveURL(/\/login/)
  await page.getByLabel('Email').fill('test@dokon.uz')
  await page.getByLabel('Parol').fill('testparol123')
  await page.getByRole('button', { name: 'Kirish' }).click()

  // qaytib keldi
  await page.getByLabel('Manzil').fill('Toshkent, Amir Temur 1')
  await page.getByRole('button', { name: 'Buyurtma berish' }).click()

  await expect(page.getByRole('heading', { name: /Buyurtma qabul qilindi/ })).toBeVisible()
})

Autentifikatsiyani qayta ishlatish

Har bir testda login qilish — sekin. Bir marta kirib, holatni saqlash:

// e2e/auth.setup.ts
import { test as setup, expect } from '@playwright/test'

setup('login', async ({ page }) => {
  await page.goto('/login')
  await page.getByLabel('Email').fill(process.env.E2E_EMAIL!)
  await page.getByLabel('Parol').fill(process.env.E2E_PAROL!)
  await page.getByRole('button', { name: 'Kirish' }).click()
  await expect(page).toHaveURL('/dashboard')
  await page.context().storageState({ path: 'e2e/.auth/user.json' })
})
// playwright.config.ts
projects: [
  { name: 'setup', testMatch: /auth\.setup\.ts/ },
  {
    name: 'chromium',
    use: { ...devices['Desktop Chrome'], storageState: 'e2e/.auth/user.json' },
    dependencies: ['setup'],
  },
],

e2e/.auth/ ni .gitignore ga qo'shing — unda sessiya cookie si.

[!WARNING] E2E testlarni production bazasiga qarshi ishga tushirmang — testlar buyurtma yaratadi, foydalanuvchilarni o'zgartiradi. Alohida test bazasi, har bir ishga tushirishdan oldin seed (boshlang'ich ma'lumotlar), tashqi to'lov va SMS xizmatlari — test rejimida yoki mock (64-dars).


Tarmoqni mock qilish

test('API xatosida xabar ko\'rsatiladi', async ({ page }) => {
  await page.route('**/api/tavsiyalar', (route) =>
    route.fulfill({ status: 500, body: JSON.stringify({ xato: 'server' }) })
  )
  await page.goto('/')
  await expect(page.getByText('Tavsiyalarni yuklab bo\'lmadi')).toBeVisible()
})

page.route — faqat brauzerdan ketadigan so'rovlarni ushlaydi. Server Component lardagi fetch lar serverda — ular uchun MSW (64-dars) yoki test API.


CI da

npm init playwright yaratgan .github/workflows/playwright.yml:

- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
- uses: actions/upload-artifact@v4
  if: ${{ !cancelled() }}
  with:
    name: playwright-report
    path: playwright-report/

Xato bo'lsa — hisobot va trace ni yuklab olib, Playwright Trace Viewer da har bir qadamni (DOM, tarmoq, konsol) ko'rish mumkin.

[!TIP] E2E testlar sekin va qimmat. Faqat muhim oqimlarni yozing: ro'yxatdan o'tish, kirish, to'lov, asosiy qidiruv. Detallarni unit va komponent testlarga qoldiring.


Tez-tez uchraydigan xatolar

1. waitForTimeout(3000)

Qattiq kutish — testlar sekin va beqaror. expect(...).toBeVisible() o'zi kutadi.

2. CSS selektorlar

page.locator('.btn-primary > span') — dizayn o'zgarsa buziladi. Rol va label bo'yicha.

3. Development serverga qarshi test

npm run dev — boshqa xatti-harakat (kesh, xatolar overlay). build && start.


Xulosa

E2E — haqiqiy brauzer, haqiqiy server, butun oqim
npm init playwright@latest; webServer: build && start
page.goto, getByRole/getByLabel, click, fill, expect(...).toBeVisible/toHaveURL
Avtomatik kutish — waitForTimeout yo'q
storageState — bir marta login, qayta ishlatish
Test bazasi; production ga qarshi emas
page.route — brauzer so'rovlarini mock; CI da trace va hisobot
Faqat muhim oqimlar

Keyingi dars

64-dars: MSW — API mock qilish Next.js da

Tashqi API larni testlarda va development da soxta javoblar bilan almashtirish.