Məzmuna keç

Modul 5: Avtomatik gözləmə və flaky testlər

Veb-first assertion-ların avtomatik olaraq necə yenidən cəhd etdiyini artıq bilirsiniz. İndi isə etibarlılığın digər tərəfinə baxaq: Playwright hər hansı bir əməliyyatı icra etməzdən əvvəl nə edir — və qalan hər waitForTimeout çağırışını həqiqətən işləyən bir şeylə necə əvəz etmək olar.

🎬 Video tezliklə əlavə olunacaq


Playwright hər əməliyyatdan əvvəl avtomatik necə gözləyir

Bölmə: “Playwright hər əməliyyatdan əvvəl avtomatik necə gözləyir”

Hər Playwright əməliyyatı — click(), fill(), hover(), press(), selectOption(), check(), uncheck() — həqiqətən icra etməzdən əvvəl bir sıra actionability yoxlamaları keçirir. Bu yoxlamalar avtomatik baş verir; siz onları yazmırsınız.

Yoxlamalar bunlardır:

YoxlamaMənası
AttachedElement DOM-da mövcuddur
VisibleElement gizlənməyib (display:none, visibility:hidden, opacity:0, sıfır ölçü)
StableElement iki ardıcıl animasiya çərçivəsi boyunca eyni sərhəd qutusunu saxlayır (hərəkəti/animasiyası dayanıb)
Receives eventsHeç bir element hədəfi örtmür (overlay, modal və ya tooltip yoxdur)
EnabledElement disabled deyil

Playwright hamısı keçənə — ya da əməliyyat vaxt aşımı bitənə (defolt: 30 saniyə) qədər dövrə üzərindən ~50 ms-dən bir bütün beş şərti yoxlayır.

// Playwright düymə attached, visible, stable, hadisə qəbul edir
// VƏ enabled olana qədər gözləyir — sonra click edir.
await page.getByRole('button', { name: 'Sifariş ver' }).click();
// Bunları özünüz yazmanıza gərək yoxdur:
// await page.waitForSelector('button', { state: 'visible' }); // artıqdır
// await page.waitForTimeout(500); // anti-pattern

Bu praktikada nə demək olar:

  • Düymə əvvəlcə yükləmə overlay-ının arxasındadırsa, click() overlay-ın yox olmasını gözləyir.
  • Düymə disabled başlayır və forma doldurulduqdan sonra aktiv olursa, click() gözləyir.
  • Element hazırda animasiya edirsə (sürüşərək girən, silinərək görünən), click() sabitləşməsini gözləyir.

Buna görə Playwright ilə açıq gözləmələrin əksəriyyəti lazımsızdır.

Yadda saxla: hər əməliyyat işə düşməzdən əvvəl attached + visible + stable + receives-events + enabled gözləyir — əməliyyatdan əvvəl nadir hallarda açıq gözləmə lazım olur.


waitForTimeout həmişə anti-pattern-dir

Bölmə: “waitForTimeout həmişə anti-pattern-dir”

page.waitForTimeout(N) tam olaraq bir şey edir: testi N millisaniyə dayandırır. Heç bir şərti yoxlamır. Tətbiqin hazır olduğunu bilmir. Həmişə yanlışdır.

// ANTİ-PATTERN — heç vaxt belə etməyin
await page.click('#submit');
await page.waitForTimeout(3000); // səhifənin 3 saniyədə yüklənəcəyini ümid edir
const text = await page.locator('h1').textContent();
expect(text).toContain('Təsdiqləndi');
// DÜZGÜN — faktiki şərti gözləyir
await page.click('#submit');
await expect(page.locator('h1')).toContainText('Təsdiqləndi');

waitForTimeout testləri nə üçün flaky edir:

  1. Sizin maşınınız o qədər sürətli ola bilər ki, 3000 ms kifayətdir. CI serveri 4× daha yavaşdır. Test CI-də uğursuz olur.
  2. Tətbiq növbəti buraxılışda sürətlənir. 3000 ms yuxumanız indi hər icrada 2 saniyə israf edir.
  3. Bir reqressiya nəticəsində tətbiq yavaşlayır. Yuxumanız indi çox qısadır. Test qırmızıya çevrilir.

waitForTimeout testinizi hardware sürətinə bağlayır. Veb-first assertion-lar və ağıllı gözləmə API-ləri testinizi tətbiq davranışına bağlayır. İkincisi heç vaxt yanlış səbəblərdən qırılmır.

waitForTimeout-un yeganə qəbul edilə bilən istifadəsi debug zamanıdır — brauzerın nə etdiyini görmək üçün testi müvəqqəti olaraq yavaşlatmaq. Commit etməzdən əvvəl onu silin.

// Debug zamanı müvəqqəti olaraq tamamdır
await page.waitForTimeout(5000); // COMMIT ETMƏZDƏN ƏVVƏL SİLİN
// Şərh xatırladıcınızdır. Həqiqətən silin.

Yadda saxla: sabit yuxuma heç nəyi yoxlamır və testi hardware sürətinə bağlayır — onu faktiki şərti gözləməklə əvəz et.


expect avtomatik yenidən cəhdi — xatırlatma

Bölmə: “expect avtomatik yenidən cəhdi — xatırlatma”

Modul 4-dən: expect(locator) assertion-ları avtomatik yenidən cəhd edir. Bu burada aktualdır çünki geliştiricilerin waitForTimeout-a əl atmasının ən yaygın səbəbi yenidən cəhd etməyən assertion istifadə etmələridir.

// Yenidən cəhd etmir — bir dəfə yoxlayır, yavaş səhifələrdə uğursuz olur
const text = await page.locator('.status').textContent();
expect(text).toBe('Hazır');
// Avtomatik yenidən cəhd edir — şərt keçənə ya da vaxt aşımı bitənə qədər yenidən cəhd edir
await expect(page.locator('.status')).toHaveText('Hazır');

Mətn, say, URL ya da görünürlük vəziyyətini yoxlamadan əvvəl waitForTimeout yazdığınızı görürsünüzsə — yoxlamanı veb-first ekvivalenti ilə əvəz edin. Yuxuma avtomatik yox olur.

Yadda saxla: əksər waitForTimeout-lar yenidən cəhd etməyən assertion səbəbindən var — veb-first expect(locator) qoy, yuxuma yox olur.


page.waitForURL() brauzerın hazırkı URL-inin gözlənilən dəyərlə uyğunlaşmasını gözləyir. String, glob şablonu ya da müntəzəm ifadə qəbul edir.

// Dəqiq URL gözləyin
await page.waitForURL('https://example.com/dashboard');
// Glob şablon gözləyin
await page.waitForURL('**/orders/**');
// Regex — dinamik ID-lər üçün faydalıdır
await page.waitForURL(/\/orders\/\d+/);

Kritik şablon: tetikle, sonra gözlə. Əvvəlcə URL-i gözləməyin — naviqasiya artıq baş vermiş ola bilər. waitForURL çağırışı onu tetikleyen əməliyyatı izləməlidir.

await page.getByRole('button', { name: 'Sifariş ver' }).click();
await page.waitForURL(/\/orders\/\d+/);
// URL artıq /orders/1042 kimi bir şeydir

waitForURL vs expect(page).toHaveURL():

Hər ikisi işləyir. Fərq niyyətdədir:

  • waitForURL açıq bir gözləmədir: “Naviqasiyanın baş verməsini gözləyirəm.”
  • expect(page).toHaveURL() bir assertion-dır: “Naviqasiyanın baş verdiyini yoxlayıram.”

Praktikada demək olar ki, eynidir — toHaveURL da yenidən cəhd edir. Test assertion-larında toHaveURL, birbaşa assertion etmədiyiniz köməkçi metodlarda ya da page object-lərdə waitForURL üstün tutun.

// Testdə — assertion formasını istifadə edin
await checkoutPage.placeOrder();
await expect(page).toHaveURL(/\/orders\/\d+/);
// Page object köməkçisində — gözləmə formasını istifadə edin
async placeOrder() {
await this.placeOrderBtn.click();
await this.page.waitForURL(/\/orders\/\d+/);
}

Yadda saxla: əvvəl tetiklə, sonra gözlə; testlərdə toHaveURL, köməkçilərdə və page object-lərdə waitForURL üstün tut.


page.waitForResponse() brauzer tərəfindən müəyyən bir HTTP cavabının qəbul edilməsini gözləyir. Bir əməliyyat şəbəkə sorğusunu tetiklediyinde istifadə edin və davam etməzdən əvvəl cavabı yoxlamanız lazımdır.

Kritik şablon: Promise.all.

waitForResponse-u əməliyyatdan sonra çağırırsınızsa, cavabı qaçıra bilərsiniz — dinləyiciniz qeydiyyata alınmazdan əvvəl tamamlanmış ola bilər. Həmişə Promise.all istifadə edərək əməliyyatı tetiklemeden əvvəl dinləyicini başladın:

// YANLIŞ — cavab dinləyicidən əvvəl gələ bilər
await page.click('.daha-yüklə');
const response = await page.waitForResponse('**/api/products');
// DÜZGÜN — dinləyici click-dən əvvəl qeydiyyata alınır
const [response] = await Promise.all([
page.waitForResponse('**/api/products'),
page.click('.daha-yüklə'),
]);
expect(response.ok()).toBeTruthy();

Uyğunlaşdırma şablonları: URL string, glob ya da cavab obyektini qəbul edən funksiyanla uyğunlaşdıra bilərsiniz.

// Glob ilə uyğunlaşdır
const [response] = await Promise.all([
page.waitForResponse('**/api/products**'),
page.click('.məhsulları-yüklə'),
]);
// Funksiya ilə uyğunlaşdır — status kodu yoxlamaları üçün faydalıdır
const [response] = await Promise.all([
page.waitForResponse(
(resp) => resp.url().includes('/api/products') && resp.status() === 200
),
page.click('.məhsulları-yüklə'),
]);
// Cavab gövdəsini yoxlayın
const body = await response.json();
expect(body.products).toHaveLength(10);

waitForResponse nə vaxt istifadə edilər:

  • “Daha yüklə”yə kliklədikdən sonra — yeni elementləri iddia etməzdən əvvəl API cavabını gözləyin
  • Forma göndərdikdən sonra — təsdiq mesajını iddia etməzdən əvvəl POST cavabını gözləyin
  • Şəbəkə xətası işləmini sınaqdan keçirərkən — 4xx ya da 5xx cavabını gözləyin

Yadda saxla: dinləyicini əməliyyatdan əvvəl Promise.all ilə qeydiyyata al, yoxsa cavab sən dinləməyə başlamazdan əvvəl gələ bilər.


page.waitForLoadState() səhifənin müəyyən bir yüklənmə vəziyyətinə çatmasını gözləyir. Üç vəziyyət var:

VəziyyətNə vaxt işə düşür
'load'load hadisəsi işə düşdü (bütün resurslar endirildi)
'domcontentloaded'DOMContentLoaded hadisəsi işə düşdü (HTML ayrıştırıldı, şəkillər hələ yox)
'networkidle'500 ms boyunca şəbəkə sorğusu yoxdur
// Səhifənin tam yüklənməsini gözləyin
await page.goto('/dashboard');
await page.waitForLoadState('load'); // adətən artıqdır — goto() artıq gözləyir
// DOM-un ayrıştırılmasını gözləyin ('load'-dan daha sürətli)
await page.waitForLoadState('domcontentloaded');
// Şəbəkənin sabitləşməsini gözləyin
await page.waitForLoadState('networkidle');

Vacib: page.goto() defolt olaraq artıq 'load'-u gözləyir, buna görə goto()-dan sonra waitForLoadState('load') çağırmaq demək olar ki, həmişə artıqdır.

Yadda saxla: goto() artıq load-u gözləyir — vəziyyətə yalnız konkret olaraq domcontentloaded və ya (nadir hallarda) networkidle lazım olanda müraciət et.


networkidle-ın çatışmazlıqları

Bölmə: “networkidle-ın çatışmazlıqları”

networkidle ən təhlükəsiz seçim kimi görünür — “hər şey bitənə qədər gözlə.” Praktikada ciddi problemləri var:

Problem 1: Heç vaxt işə düşməyə bilər. Müasir SPA-lar fasiləsiz olaraq arxa planda sorğular edir — analitika pingi, heartbeat polling, WebSocket yenidən qoşulmaları. Tətbiq sorğu göndərməyə davam edərsə, networkidle sonsuz gözləyir (vaxt aşımına qədər).

Problem 2: Yavaşdır. Sakit səhifələrdə belə, networkidle həmişə son sorğudan sonra minimum 500 ms gözləyir. Bunu yüzlərlə testdə artırın.

Problem 3: Yanlış şeyi gözləyir. Adətən müəyyən bir cavabla maraqlanırsınız, “bütün şəbəkə fəaliyyəti” ilə deyil. Başqa bir arxa planda çalışan sorğudan gələn cavab doğru anda networkidle-ı açsa, həqiqətən maraqlandığınız cavab gəlməzdən əvvəl baş verə bilər.

// KÖVRƏK — şəbəkə sakinləşdikdə işə düşür, datanız hazır olduqda deyil
await page.click('.məhsulları-yüklə');
await page.waitForLoadState('networkidle');
await expect(page.locator('.product-card')).toHaveCount(10);
// MÖHKƏM — müəyyən cavab gəldikdə işə düşür
const [response] = await Promise.all([
page.waitForResponse('**/api/products'),
page.click('.məhsulları-yüklə'),
]);
await expect(page.locator('.product-card')).toHaveCount(10);

Playwright-in öz sənədləri networkidle-ı tövsiyə edilməyən kimi qeyd edir — məhz bu səbəblərdən. Köhnə nümunələrdə və ya təlimatlarda görsən, onu ən yaxşı təcrübə deyil, kod iyi kimi qəbul et.

networkidle-ın qəbul edilə bilən olduğu zaman:

  • Arxa planda sorğu olmayan sadə statik səhifə yüklənməsi
  • SPA marşrutlaması olmayan köhnəlmiş tətbiqlərdə testlər
  • Daha yaxşı siqnal olmadıqda son çarə kimi — hətta o zaman belə, nə üçün olduğunu izah edən şərh əlavə edin

Yadda saxla: maraqlandığın konkret cavabı gözlə, bütün şəbəkənin sakitləşməsini deyil — SPA-da networkidle heç vaxt gəlməyə bilər.


Flaky testləri diaqnostika etmək

Bölmə: “Flaky testləri diaqnostika etmək”

Test heç bir kod dəyişikliyi olmadan bəzən keçdikdə, bəzən isə uğursuz olduqda flaky-dir. Flakiness demək olar ki, həmişə kök səbəbə malikdir — onu tapın.

Addım 1: Yuxumaları yoxlayın

Bölmə: “Addım 1: Yuxumaları yoxlayın”

Ən yaygın səbəb. Test faylında waitForTimeout axtarın. Hər nümunəni uyğun veb-first alternativlə əvəz edin.

// Əvvəl
await page.click('#submit');
await page.waitForTimeout(2000);
const text = await page.locator('.result').textContent();
// Sonra
await page.click('#submit');
await expect(page.locator('.result')).toHaveText(/uğur/i);

Addım 2: Yenidən cəhd etməyən assertion-ları yoxlayın

Bölmə: “Addım 2: Yenidən cəhd etməyən assertion-ları yoxlayın”

Lokatorlar əvəzinə await-lənmiş dəyərlərdən qurulan assertion-ları axtarın.

// Flaky — bir dəfə qiymətləndirir
const visible = await page.locator('.banner').isVisible();
expect(visible).toBe(true);
// Sabit — avtomatik yenidən cəhd edir
await expect(page.locator('.banner')).toBeVisible();

Addım 3: Qorunmamış URL ya da cavab yoxlamalarını tapın

Bölmə: “Addım 3: Qorunmamış URL ya da cavab yoxlamalarını tapın”
// Flaky — page.url() sinxrondur, yenidən cəhd yoxdur
expect(page.url()).toContain('/dashboard');
// Sabit — avtomatik yenidən cəhd edir
await expect(page).toHaveURL('/dashboard');

Addım 4: waitForResponse-da yarış şərtlərini yoxlayın

Bölmə: “Addım 4: waitForResponse-da yarış şərtlərini yoxlayın”

Ən incə səbəb. waitForResponse tetikleyen əməliyyatdan sonra çağırılırsa, cavabı qaçıra bilər.

// Flaky — cavab dinləyicidən əvvəl gəlmiş ola bilər
await page.click('.axtarış');
const resp = await page.waitForResponse('**/api/search');
// Sabit — tetiklemeden əvvəl dinləyici qeydiyyata alınır
const [resp] = await Promise.all([
page.waitForResponse('**/api/search'),
page.click('.axtarış'),
]);

Addım 5: Click-lərdə { force: true } yoxlayın

Bölmə: “Addım 5: Click-lərdə { force: true } yoxlayın”

click({ force: true }) bütün beş actionability yoxlamasını atlayır. Elementi örtülü, deaktiv ya da gizli olsa belə tıklamağa məcbur edir. Bu real problemləri gizlədir.

// Kod iyi — bu element nə üçün normal tıklana bilmir?
await page.locator('.submit-btn').click({ force: true });
// Düzəliş: tıklamanı bloklayan şeyi tapın və idarə edin
await expect(page.locator('.modal')).not.toBeVisible(); // modalın bağlanmasını gözləyin
await page.locator('.submit-btn').click();

Flaky test diaqnostika cədvəli

Bölmə: “Flaky test diaqnostika cədvəli”
SimptomKök səbəbDüzəliş
Yerli keçir, CI-də uğursuz olurwaitForTimeout sürətli maşına uyğunlaşdırılıbVeb-first assertion ilə əvəz edin
Eyni maşında ~20% uğursuz olurYenidən cəhd etməyən assertion, yarış şərtiYenidən cəhd etməyən yoxlamanı tapın
Yalnız testlər paralel çalışdıqda uğursuz olurPaylaşılan vəziyyət, test sıralama asılılığıTest datanı izolyasiya edin
Heç bir test dəyişikliyi olmadan deploy-dan sonra uğursuz olurLokator çox kövrəkdirRol əsaslı ya da data-testid lokatorlarına keçin
console.log əlavə etdiyinizdə keçirƏlavə mikrotask tick-i ilə üzə çıxan yarış şərtiDüzgün await ya da Promise.all əlavə edin
Həmişə eyni addımda uğursuz olurReal bug, flakiness deyilTətbiqi ya da assertion məntiqi düzəldin

Yadda saxla: flakiness-in kök səbəbi var — yuxuma əlavə etmək əvəzinə yoxlama siyahısını keç (yuxumalar, yenidən cəhd etməyən assertion-lar, sinxron URL yoxlamaları, çatışmayan Promise.all, { force: true }).


Xüsusi əməliyyat vaxt aşımları

Bölmə: “Xüsusi əməliyyat vaxt aşımları”

Defolt olaraq, hər əməliyyat actionability yoxlamaları üçün 30 saniyəyə qədər gözləyir. Bunu qlobal olaraq ya da əməliyyat-başına dəyişdirə bilərsiniz.

// Əməliyyat-başına vaxt aşımı (millisaniyə)
await page.getByRole('button', { name: 'Yavaş əməliyyat' }).click({ timeout: 60_000 });
// playwright.config.js-də qlobal əməliyyat vaxt aşımı
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
actionTimeout: 10_000, // bütün əməliyyatlar üçün 10 saniyə
},
});

Bir şeyin sürətli olması lazım olduğunu bildiyiniz zaman vaxt aşımını azaldın. Düymə tıklaması 2 saniyə içində naviqasiyaya səbəb olmalıdırsa, amma 30 saniyelik vaxt aşımınız varsa, real reqressiya test uğursuz olmazdan əvvəl 30 saniyə aşkar edilməyə bilər.

Yadda saxla: sürətli olmalı şeylərdə vaxt aşımını azalt ki, real reqressiya 30 s asılı qalmaqdansa tez uğursuz olsun.


Ağıllı gözləmə yaddaş vərəqi

Bölmə: “Ağıllı gözləmə yaddaş vərəqi”
Nəyi gözləmək istəyirəm…İstifadə et
Element / mətn / say şərtiveb-first expect(locator)… (default seçimin)
Naviqasiya və ya yönləndirməexpect(page).toHaveURL(…) (test) · page.waitForURL(…) (köməkçi)
Konkret şəbəkə cavabıPromise.all([page.waitForResponse(…), əməliyyat])
DOM ayrıştırılıb (şəkillər yox)page.waitForLoadState('domcontentloaded')
Bütün şəbəkə sakitləşsin (son çarə)page.waitForLoadState('networkidle')
Sabit gecikmə❌ heç vaxt waitForTimeout — yalnız debug üçün


Bu tapşırıqlar http://localhost:3000-də yerli olaraq işləyən TestMarket Lab istifadə edir.


Tapşırıq 1: Sabit gözləmələri veb-first assertion-larla əvəz edin

Bölmə: “Tapşırıq 1: Sabit gözləmələri veb-first assertion-larla əvəz edin”

Bu testi refaktor edin — hər waitForTimeout-u düzgün veb-first alternativlə əvəz edin:

// ƏVVƏL (flaky — hər yerdə sabit yuxular)
import { test, expect } from '@playwright/test';
test('məhsul axtarışı', async ({ page }) => {
await page.goto('/products');
await page.waitForTimeout(1500);
await page.fill('#search', 'Wireless');
await page.waitForTimeout(800);
await page.click('#apply-filters');
await page.waitForTimeout(3000);
const count = await page.locator('.product-card').count();
expect(count).toBeGreaterThan(0);
await page.waitForTimeout(500);
expect(page.url()).toContain('search=Wireless');
});
Əvəz edinBunla
waitForTimeout(1500)Silin — fill() artıq element üçün avtomatik gözləyir
waitForTimeout(800)Silin — click() artıq avtomatik gözləyir
waitForTimeout(3000) + count()expect(locator).not.toHaveCount(0)
waitForTimeout(500) + page.url()expect(page).toHaveURL(/[?&]search=Wireless/)
// SONRA (veb-first — sabit yuxular yoxdur)
import { test, expect } from '@playwright/test';
test('məhsul axtarışı', async ({ page }) => {
await page.goto('/products');
await page.locator('#search').fill('Wireless');
await page.locator('#apply-filters').click();
await expect(page.locator('.product-card')).not.toHaveCount(0);
await expect(page).toHaveURL(/[?&]search=Wireless/);
});

Tapşırıq 2: Flaky naviqasiya testini düzəldin

Bölmə: “Tapşırıq 2: Flaky naviqasiya testini düzəldin”

Orijinal iki səbəbdən flaky-dir: URL-i sinxron yoxlayır (yenidən cəhd yoxdur) sabit yuxudan sonra, və quraşdırması natamamdır — /checkout daxil olmuş, səbətində element olan istifadəçi tələb edir, ona görə sadə goto('/checkout') sadəcə login səhifəsinə qayıdır.

// ƏVVƏL (flaky — və checkout-a heç çatmır)
await page.goto('/checkout');
await fillCheckoutForm(page);
await page.click('#place-order');
await page.waitForTimeout(2000);
expect(page.url()).toMatch(/\/orders\/\d+/); // sinxron — yenidən cəhd yoxdur

Öz-özünə yetərli, veb-first yenidən yazılış:

// SONRA
import { test, expect } from '@playwright/test';
test.describe('Ödəniş yönləndirməsi', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/auth/login');
await page.locator('#email').fill('customer@test.io');
await page.locator('#password').fill('customer123');
await page.getByTestId('login-submit').click();
await page.goto('/products');
await page.getByTestId('add-to-cart').first().click();
await page.goto('/checkout');
});
test('sifariş vermək sifariş səhifəsinə yönləndirir', async ({ page }) => {
await page.locator('#shipping_name').fill('Jane Doe');
await page.locator('#shipping_address').fill('123 Main Street');
await page.locator('#shipping_city').fill('Portland');
await page.locator('#shipping_zip').fill('97201');
// Click avtomatik gözləyir; toHaveURL yönləndirmə gələnə qədər yenidən cəhd edir
await page.getByTestId('place-order').click();
await expect(page).toHaveURL(/\/orders\/\d+/);
});
});

Tapşırıqlar:

  • toHaveURL-u köhnə expect(page.url()).toMatch(/\/orders\/\d+/) ilə əvəz edin və bir neçə dəfə işlədin — flaky edən məhz sinxron yoxlama idi.
  • Page-object köməkçisində bunun əvəzinə await page.waitForURL(/\/orders\/\d+/) yazardınız — hər ikisi gözləyir; assertion forması sadəcə həm də yoxlama rolunu oynayır.

Tapşırıq 3: Avtomatik gözləmə və waitForResponse

Bölmə: “Tapşırıq 3: Avtomatik gözləmə və waitForResponse”

TestMarket Lab məhsullar səhifəsini ?delay=NNNN query parametri (millisaniyə) ilə qəsdən yavaşlada bilər — avtomatik gözləməni bir dənə də yuxu olmadan görmək üçün ideal yol. Sonra cavabı Promise.all şablonu ilə tut.

import { test, expect } from '@playwright/test';
test.describe('Real tətbiqi gözləmək', () => {
test('veb-first assertion yavaş səhifəni gözləyir — yuxu lazım deyil', async ({ page }) => {
// ?delay=2000 serverin cavabı 2s saxlamasına səbəb olur
await page.goto('/products?delay=2000');
// waitForTimeout yoxdur: assertion sadəcə kartlar render olana qədər gözləyir
await expect(page.locator('.product-card')).not.toHaveCount(0);
});
test('cavabı Promise.all ilə tut', async ({ page }) => {
// Dinləyici onu tetikleyen naviqasiyadan ƏVVƏL qeydiyyata alınır
const [response] = await Promise.all([
page.waitForResponse(
(resp) => resp.url().includes('/products') && resp.request().method() === 'GET'
),
page.goto('/products?search=Wireless'),
]);
expect(response.ok()).toBeTruthy();
await expect(page.locator('.product-card')).not.toHaveCount(0);
});
});

Tapşırıqlar:

  • Birinci testi ?delay=4000-ə qaldırın — yenə waitForTimeout olmadan keçir; Playwright sadəcə daha çox gözləyir.
  • waitForResponse-u goto-dan sonraya keçirin (Promise.all-dan kənara) — sürətli cavabda dinləyici onu qaçıra bilər və test asılı qalır. Buna görə əvvəl qeydiyyata alırsan.

  1. Playwright hər əməliyyatdan əvvəl hansı beş actionability yoxlamasını aparır?
  2. waitForTimeout yerli keçdikdə belə CI-də testləri flaky niyə edir?
  3. waitForResponse üçün Promise.all şablonu nədir və nə üçün zəruridir?
  4. waitForURL ilə expect(page).toHaveURL() arasındakı fərq nədir?
  5. networkidle nə üçün SPA-lar üçün etibarsızdır?
  6. click({ force: true }) olan bir test görürsünüz. Bu sizə test haqqında nə deyir?
  7. Yerli keçən amma CI-də 20% uğursuz olan bir testi necə diaqnostika edərdiniz?
  8. Defolt əməliyyat vaxt aşımı nədir və onu əməliyyat-başına necə dəyişdirirsiniz?

  1. Avtomatik gözləmə daxildir. Hər Playwright əməliyyatı elementin attached, visible, stable, bloklanmamış və enabled olmasını gözləyir. Əməliyyatlardan əvvəl açıq gözləmə yazmağa nadir hallarda ehtiyac var.
  2. waitForTimeout həmişə yanlışdır. Heç bir şərti yoxlamır. Hər yuxumanı veb-first assertion ya da ağıllı gözləmə API-si ilə əvəz edin.
  3. waitForResponse üçün Promise.all. Tetikleyen əməliyyatdan əvvəl dinləyicini qeydiyyata alın — heç vaxt sonra deyil.
  4. SPA-larda networkidle-dan çəkinin. Heç vaxt gəlməyə bilən sükutu gözləyir. Bunun əvəzinə müəyyən bir URL ya da cavabı gözləyin.
  5. Flakiness-in kök səbəbi var. Diaqnostika yoxlama siyahısı üzərindən işləyin: yuxumalar, yenidən cəhd etməyən assertion-lar, sinxron URL yoxlamaları, çatışmayan Promise.all ya da { force: true }.