Geonode logo
Geonode Team

Geonode Team

Diperbarui: 7 Oktober 2026

Diterbitkan: 2 September 2026

Masalah Timeout pada Playwright: Cara Kerjanya dan Cara Mengatasinya

"Timeout 30.000 ms terlampaui" adalah kesalahan Playwright yang paling umum dan paling sering salah didiagnosis. Hal ini jarang berarti tes Anda berjalan lambat. Biasanya, hal ini berarti suatu elemen tidak pernah menjadi dapat diakses, dan batas waktu tersebut hanyalah titik di mana Playwright berhenti menunggu kondisi yang tidak akan pernah terpenuhi. Panduan ini membahas enam jenis batas waktu yang berbeda, mana yang sebenarnya Anda alami, dan mengapa menaikkan batas waktu biasanya bukanlah solusi yang tepat.

Sudut pandang kami, secara gamblang: kami adalah Geonode dan kami menjual layanan proxy, dan Playwright sering dijalankan melalui layanan tersebut untuk keperluan scraping dan pengujian geografis. Kenyataannya, sebagian besar kasus timeout Playwright sama sekali tidak ada hubungannya dengan proxy. Selektor yang tidak cocok dengan apa pun, elemen yang tertutupi oleh banner cookie, animasi yang tidak pernah selesai — semua ini akan menghasilkan kesalahan yang sama, baik lalu lintas Anda langsung maupun melalui enam perantara. Ada satu kasus nyata yang terkait dengan proxy, yang dibahas di bagian akhir: koneksi residensial menambah latensi nyata, sehingga pengaturan default yang disesuaikan untuk pengujian lokal dapat menyebabkan kegagalan palsu. Namun, periksa selektornya terlebih dahulu. Jika pengujian Anda gagal dengan cara yang sama tanpa konfigurasi proxy, maka proxy bukanlah masalahnya.

Enam Jenis Timeout dan Nilai Defaultnya

Hal pertama yang perlu dipahami adalah bahwa ini adalah mekanisme terpisah dengan nilai default yang berbeda-beda, dan mengetahui mana yang terpicu akan memberi tahu Anda di mana harus mencari penyebabnya.

TimeoutDefaultDisetel melalui
Test30.000 mstestConfig.timeout, test.setTimeout()
Expect5.000 mstestConfig.expect.timeout, opsi per-assertion
ActionTanpa batas waktutestOptions.actionTimeout, opsi per-panggilan
NavigationTanpa batas waktutestOptions.navigationTimeout, opsi per-panggilan
hook beforeAll / afterAll30.000 mstest.setTimeout() di dalam hook
GlobalTidak adatestConfig.globalTimeout

Nilai-nilai ini diambil dari dokumentasi batas waktu Playwright.

Dua di antaranya mengejutkan banyak orang.

Aksi dan navigasi tidak memiliki batas waktu secara default. Keduanya hanya dibatasi oleh batas waktu pengujian. Jadi, panggilan page.click() yang tidak dikualifikasi akan menunggu hingga sisa anggaran pengujian habis, dan kesalahan yang Anda dapatkan adalah pengujian yang melebihi batas waktu, bukan kliknya. Itulah mengapa pesan tersebut menunjukkan 30.000 ms meskipun tidak ada yang mengonfigurasi klik selama 30 detik.

Batas waktu global sama sekali tidak memiliki nilai default. Dokumen menjelaskan tujuannya sebagai pencegahan "penggunaan sumber daya yang berlebihan ketika semuanya berjalan salah" — sebaiknya diatur dalam CI agar rangkaian tes yang macet gagal daripada terus-menerus menduduki runner.

Apa Sebenarnya Arti Pesan “Timeout of 30000ms exceeded”

Pesan tersebut merujuk pada batas waktu pengujian, dan batas waktu ini lebih merupakan batasan daripada diagnosis. Ada proses di dalamnya yang memakan waktu terlalu lama, dan pesan tersebut hanya menyebutkan batas waktunya, bukan penyebabnya.

Diurutkan berdasarkan seberapa sering masing-masing menjadi penyebab sebenarnya:

1. Sebuah locator tidak menemukan apa pun. Selektornya salah, atau elemen tersebut belum muncul, atau berada di dalam iframe atau shadow root yang tidak Anda perhitungkan. Playwright menunggu dengan sabar sesuatu yang tidak akan pernah ada.

2. Elemen tersebut ada tetapi tidak dapat diakses. Tertutupi oleh overlay, banner cookie, atau sticky header. Dinonaktifkan. Masih dalam animasi. Playwright menunggu hingga elemen tersebut dapat diklik, namun hal itu tidak pernah terjadi.

3. Navigasi tidak pernah selesai. Permintaan jaringan yang macet, loop pengalihan, atau kondisi "waitUntil" — khususnya "networkidle" — yang tidak akan pernah terpenuhi oleh halaman dengan koneksi persisten.

4. Assertion tidak pernah menjadi benar. "expect" yang memeriksa kondisi yang tidak pernah dicapai oleh aplikasi.

5. Tes tersebut memang melakukan terlalu banyak hal. Ini adalah kasus nyata, dan yang paling jarang terjadi.

Urutan kasus ini penting karena cara perbaikannya sangat berbeda. Hanya kasus 5 yang dapat diatasi dengan meningkatkan batas waktu (timeout). Pada empat kasus lainnya, meningkatkan batas waktu hanya akan membuat kita menunggu lebih lama untuk kegagalan yang sama.

Kemampuan untuk Melakukan Tindakan Adalah Alasan Mengapa Klik Anda Harus Menunggu

Memahami hal ini akan menghilangkan sebagian besar kebingungan, karena hal ini menjelaskan apa yang dilakukan Playwright selama tiga puluh detik tersebut.

Dokumentasi actionability menyatakan bahwa Playwright "melakukan serangkaian pemeriksaan actionability pada elemen-elemen sebelum melakukan tindakan untuk memastikan tindakan-tindakan tersebut berjalan sesuai harapan", dan bahwa Playwright "secara otomatis menunggu hingga semua pemeriksaan yang relevan lulus, dan baru kemudian melakukan tindakan yang diminta". Jika pemeriksaan tersebut tidak terpenuhi tepat waktu, "tindakan tersebut gagal dengan pesan kesalahan 'TimeoutError'."

Pemeriksaan yang diperlukan berbeda-beda tergantung pada tindakan, dan perbedaan tersebut bersifat diagnostik:

TindakanPemeriksaan yang diperlukan
click, dblclick, check, uncheck, tap, setCheckedterlihat, stabil, menerima peristiwa, diaktifkan
hover, dragToterlihat, stabil, menerima peristiwa
fill, clearterlihat, diaktifkan
selectOptionterlihat, diaktifkan
screenshot, selectTextterlihat
scrollIntoViewIfNeededstabil
blur, focus, press, pressSequentially, dispatchEvent, setInputFilestidak ada

Dua hal langsung terlihat dari tabel ini.

Jika click mengalami timeout sementara fill pada elemen yang sama berhasil, hal ini menandakan elemen tersebut "stabil" atau "menerima peristiwa" — elemen tersebut sedang bergerak, atau ada sesuatu di atasnya. Animasi dan overlay biasanya menjadi penyebabnya.

Tindakan tanpa pemeriksaan merupakan jalan keluar darurat sekaligus tanda peringatan. Jika locator.click() mengalami timeout tetapi dispatchEvent('click') berfungsi, Anda belum memperbaiki apa pun — Anda hanya melewati pemeriksaan yang menunjukkan bahwa pengguna sungguhan juga tidak dapat mengklik elemen tersebut. Terkadang hal itu dapat diterima. Biasanya, hal ini berarti ada masalah overlay yang sebenarnya yang tidak lagi terdeteksi oleh pengujian Anda.

Mengubah Setiap Batas Waktu di Tempat yang Tepat

Konfigurasi terdapat di beberapa tingkatan, dan menempatkannya di tingkatan yang salah akan menghasilkan hasil yang membingungkan.

Konfigurasi global, di playwright.config.ts

:

export default defineConfig({
  timeout: 60_000,
  globalTimeout: 60 * 60 * 1000,
  expect: { timeout: 10_000 },
  use: {
    actionTimeout: 15_000,
    navigationTimeout: 30_000,
  },
});

Perhatikan di mana masing-masing berada. timeout

dan globalTimeout

adalah konfigurasi tingkat atas; expect.timeout

berada di bawah expect

; actionTimeout

dan navigationTimeout

berada di bawah use

, karena keduanya merupakan opsi pengujian, bukan konfigurasi pelaksana. Menempatkannya pada tingkat yang salah akan diabaikan tanpa pemberitahuan.

Per pengujian:

test('slow one', async ({ page }) => {
  test.setTimeout(120_000);
  // ...
});

**test.slow()

** melipatgandakan batas waktu default menjadi tiga kali lipat — nilai default yang baik untuk pengujian yang Anda ketahui benar-benar memakan waktu lama tanpa harus menentukan angka sembarangan.

Per asersi:

await expect(page.getByRole('status')).toHaveText('Done', { timeout: 30_000 });

Per tindakan:

await page.getByRole('button', { name: 'Export' }).click({ timeout: 15_000 });

**Dalam beforeAll

dan afterAll

**, yang memiliki batas waktu 30 detik tersendiri, panggil test.setTimeout()

di dalam hook itu sendiri.

Untuk fixture yang lambat, berikan batas waktu tersendiri di test.extend()

daripada menambah batas waktu pada setiap tes yang menggunakannya:

export const test = base.extend<{ seeded: void }>({
  seeded: [async ({}, use) => {
    await seedDatabase();
    await use();
  }, { timeout: 60_000 }],
});

Prinsip umumnya: tetapkan cakupan paling sempit yang dapat menyelesaikan masalah. Meningkatkan batas waktu tes global untuk mengakomodasi satu tes yang lambat akan membuat setiap tes lainnya lebih lambat dalam menunjukkan kegagalan, yang menghabiskan waktu nyata dalam CI.

Apa Saja yang Diperhitungkan dalam Batas Waktu Tes

Hal ini sering disalahpahami, dan menjelaskan mengapa ada tes yang mengalami timeout "sebelum melakukan apa pun".

Dokumentasinya sangat jelas: "Waktu yang dihabiskan oleh fungsi tes, pengaturan fixture, dan hook beforeEach termasuk dalam batas waktu tes."

Jadi, sebuah beforeEach yang melakukan login, mengisi data, dan menavigasi halaman akan menghabiskan waktu 30 detik yang sama dengan yang dibutuhkan oleh badan tes Anda. Sebuah tes yang tampaknya mengalami timeout pada baris pertamanya mungkin telah menghabiskan 28 detik pada tahap penyiapan.

Fixture secara default berbagi batas waktu pengujian, yang merupakan jebakan yang sama dalam bentuk berbeda — fixture yang memakan banyak waktu akan menghabiskan alokasi waktu setiap pengujian yang bergantung padanya. Berikan fixture yang lambat batas waktu tersendiri daripada menaikkan batas waktu pengujian secara keseluruhan.

Proses pembongkaran (teardown) dipisahkan: pembongkaran fixture dan hook afterEach mendapatkan alokasi waktu tersendiri setelah fungsi tes selesai, sehingga pembongkaran yang lambat tidak menghabiskan waktu tes.

Implikasi praktisnya untuk debugging: ketika sebuah tes mengalami timeout, periksa seluruh rantai — fixture, beforeEach, dan badan tes — bukan hanya baris yang ditunjuk oleh kesalahan.

Lakukan Diagnosis Sebelum Meningkatkan Kapasitas

Urutan langkah yang dapat mengatasi sebagian besar masalah timeout dalam beberapa menit.

Jalankan dengan trace viewer. Ini adalah alat paling berharga yang justru jarang dimanfaatkan:

npx playwright test --trace on
npx playwright show-trace trace.zip

Jejak (trace) tersebut menampilkan setiap tindakan, durasinya, snapshot DOM sebelum dan sesudah, serta aktivitas jaringan. Locator yang tidak cocok dengan apa pun akan langsung terlihat; begitu pula banner cookie yang berada di atas tombol Anda.

Jalankan dalam mode headed dan diperlambat saat Anda ingin mengamati prosesnya:

npx playwright test --headed --debug

Periksa apakah locator tersebut berhasil diproses:

console.log(await page.getByRole('button', { name: 'Save' }).count());

Nilai nol menandakan masalah selektor, dan tidak ada nilai timeout yang dapat memperbaikinya.

Periksa apakah ini masalah stabilitas dengan menggunakan tindakan tanpa pemeriksaan sebagai diagnostik — bukan sebagai solusi. Jika dispatchEvent('click')

berfungsi sementara click()

mengalami timeout, berarti ada sesuatu yang menutupi atau memindahkan elemen tersebut.

**Periksa apakah ada penundaan "networkidle

".** Halaman dengan beacon analitik, websocket, atau polling mungkin tidak pernah mencapai kondisi jaringan idle. Lebih baik tunggu hal yang benar-benar Anda perlukan:

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

Baca pesan kesalahan secara lengkap. Kesalahan timeout Playwright mencakup locator, jumlah elemen yang telah diselesaikan, dan pemeriksaan actionability mana yang tertunda. Detail terakhir ini biasanya langsung menyebutkan masalahnya.

Meningkatkan Batas Waktu Biasanya Justru Memperparah Ketidakstabilan

Ini adalah bagian yang bertentangan dengan intuisi, namun penting.

Tes yang tidak stabil adalah tes yang hasilnya bergantung pada waktu. Meningkatkan batas waktu memperluas rentang waktu di mana tes tersebut lulus, sehingga ketidakstabilan menjadi lebih jarang — dan karenanya lebih sulit direproduksi, lebih sulit didiagnosis, serta prosesnya menjadi lebih lambat saat tes tersebut gagal.

Sementara itu, biaya harus dibayar setiap kali terjadi kegagalan. Satu rangkaian 200 tes dengan batas waktu 30 detik membutuhkan waktu paling lama 100 menit untuk gagal sepenuhnya; pada batas waktu 120 detik, dibutuhkan waktu 400 menit. Dalam CI, hal ini berarti biaya nyata dan waktu tunggu yang nyata.

Apa yang sebenarnya dapat mengatasi ketidakstabilan:

Tunggu statusnya, bukan waktunya. waitForTimeout hampir selalu salah. Lakukan validasi terhadap kondisi yang Anda perhatikan dan biarkan Playwright melakukan polling.

Gunakan validasi berbasis web terlebih dahulu. expect(locator).toBeVisible() akan mencoba ulang secara otomatis. expect(await locator.isVisible()).toBe(true) hanya memeriksa sekali dan langsung gagal pada kegagalan pertama — sumber ketidakstabilan yang halus namun sangat umum.

Tangani overlay secara deterministik. Tutup banner cookie dalam fixture daripada berharap banner tersebut sudah hilang.

Nonaktifkan animasi dalam konfigurasi Anda jika memungkinkan, daripada menunggu animasi tersebut selesai.

Tunggu respons jaringan spesifik yang Anda andalkan, bukan sekadar menunggu jaringan menjadi sepi.

Stabilkan data. Pengujian yang bergantung pada state bersama yang dapat diubah (mutable) rentan terhadap ketidakstabilan karena alasan yang tidak dapat diatasi oleh batas waktu (timeout).

Kapan sebaiknya benar-benar menetapkan batas waktu: operasi tersebut memang sangat lambat dan tidak ada cara lain untuk mengatasinya — pengunggahan file besar, laporan yang membutuhkan waktu satu menit untuk dibuat, profil jaringan yang sengaja dibatasi kecepatannya. Dalam kasus-kasus tersebut, tetapkan batas waktu secara spesifik, hanya pada pengujian atau pernyataan tersebut, dan biarkan pengaturan default tetap seperti semula.

Timeout Saat Menjalankan Melalui Proxy

Kondisi di mana munculnya pesan kesalahan (raise) dianggap wajar, beserta konfigurasi yang sesuai.

Playwright menerima pengaturan proxy di konfigurasi jaringan:

export default defineConfig({
  use: {
    proxy: {
      server: 'http://proxy.example.com:9000',
      username: 'user',
      password: 'pass',
    },
  },
});

Atau per konteks, yang cocok digunakan ketika tes yang berbeda memerlukan lokasi keluar yang berbeda:

const context = await browser.newContext({
  proxy: { server: 'http://proxy.example.com:9000' },
});

Tiga konsekuensi praktis.

Proksi residensial menambah latensi yang nyata. Lalu lintas keluar melalui koneksi konsumen yang sebenarnya, sehingga penambahan beberapa ratus milidetik per permintaan adalah hal yang normal, bukan kesalahan. Sebuah halaman yang melakukan delapan puluh permintaan akan mengakumulasi latensi tersebut sebanyak delapan puluh kali. Pengaturan default yang disesuaikan untuk localhost akan menghasilkan kegagalan yang tampak seperti proxy rusak, padahal sebenarnya hanya disebabkan oleh jarak.

Tanggapan yang tepat adalah mengukur daripada menebak: jalankan rangkaian tes melalui proxy, periksa waktu jejak (trace timings), dan atur ``navigationTimeout`

serta ``actionTimeout

` berdasarkan pengamatan Anda dengan margin yang cukup besar.

Bandwidth adalah biaya sesungguhnya, dan biayanya sangat besar saat menggunakan browser. Playwright memuat setiap gambar, font, skrip, dan video secara pra-muat. Pada koneksi internet rumahan yang dibatasi kuota seharga $0,79/GB, hal ini mendominasi seluruh pengeluaran Anda. Memblokir jenis sumber daya yang tidak diperlukan adalah penghematan terbesar yang tersedia:

await page.route('**/*.{png,jpg,jpeg,webp,gif,woff,woff2,mp4}', r => r.abort());

Hal ini secara rutin mengurangi lalu lintas hingga sebagian besar dari totalnya dan mempercepat pengujian Anda sebagai efek samping.

Pemblokiran bukanlah timeout. Jika target menampilkan halaman tantangan, Playwright akan mengalami timeout saat menunggu elemen yang tidak ada di halaman tantangan tersebut — yang terlihat persis seperti timeout namun sebenarnya bukan. Ambil tangkapan layar saat terjadi kegagalan dan perhatikan apa yang sebenarnya ditampilkan:

use: { screenshot: 'only-on-failure', trace: 'retain-on-failure' }

Ini adalah pola kegagalan diam-diam yang kami jelaskan dalam mengapa pengujian proxy penting: permintaan berhasil, halaman ditampilkan, namun halaman tersebut salah.

Pertanyaan Lainnya

Berapa batas waktu default di Playwright?

30.000 ms untuk sebuah tes dan untuk hook beforeAll/afterAll, serta 5.000 ms untuk pernyataan expect. Batas waktu aksi dan navigasi tidak memiliki nilai default dan hanya dibatasi oleh batas waktu tes; itulah sebabnya klik yang lambat melaporkan batas waktu tes sebesar 30 detik, bukan batas waktunya sendiri.

Bagaimana cara meningkatkan batas waktu untuk satu tes Playwright?

Panggil test.setTimeout(120_000) di dalam tes, atau test.slow() untuk melipatgandakan batas waktu default menjadi tiga kali lipat. Gunakan metode ini daripada menaikkan batas waktu global, yang akan membuat tes lainnya lebih lambat gagal.

Mengapa tes Playwright saya mengalami timeout padahal elemennya ada di halaman?

Biasanya karena elemen tersebut tidak dapat diklik. Perintah click mengharuskan elemen tersebut terlihat, stabil, menerima peristiwa, dan diaktifkan — sehingga elemen yang tertutup oleh banner, atau masih dalam animasi, akan terdeteksi tetapi tidak akan pernah diklik. Pesan kesalahan akan menyebutkan pemeriksaan mana yang tertunda.

Apa perbedaan antara batas waktu tes (test timeout) dan batas waktu ekspektasi (expect timeout)?

Timeout tes adalah total batas waktu untuk fungsi tes, pengaturan fixture, dan hook beforeEach secara gabungan, dengan nilai default 30 detik. Timeout expect adalah lamanya waktu yang digunakan oleh satu asersi web-first untuk melakukan polling, dengan nilai default 5 detik. Asersi yang gagal setelah 5 detik disebabkan oleh timeout expect, bukan timeout tes.

Apakah saya harus menggunakan waitForTimeout di Playwright?

Hampir tidak pernah. Waktu tunggu tetap biasanya terlalu singkat, sehingga membuat pengujian menjadi tidak stabil, atau terlalu lama, sehingga memperlambat rangkaian pengujian — biasanya keduanya terjadi pada mesin yang berbeda. Sebaiknya tunggu hingga kondisi terpenuhi dengan menggunakan asersi web-first, yang akan mencoba ulang secara otomatis hingga batas waktu habis.

Mengapa networkidle tidak pernah terpenuhi?

Karena halaman terus mengirimkan permintaan — beacon analitik, websocket, polling, koneksi jangka panjang. networkidle memerlukan kondisi tenang, dan banyak aplikasi modern tidak pernah berada dalam kondisi tenang. Sebaiknya tunggu elemen atau respons spesifik yang Anda perlukan.

Apakah menaikkan batas waktu (timeout) dapat memperbaiki tes yang tidak stabil?

Hal itu hanya menyembunyikannya. Ketidakstabilan menjadi lebih jarang, lebih sulit direproduksi, dan lebih lambat untuk gagal, sementara setiap kegagalan yang sebenarnya dalam rangkaian pengujian kini memakan waktu lebih lama. Perbaiki penyebabnya — tunggu hingga status tercapai daripada menunggu waktu, gunakan asersi yang mencoba ulang, tutup overlay secara deterministik, dan nonaktifkan animasi.

Apakah saya memerlukan batas waktu yang lebih lama saat menggunakan proxy?

Seringkali ya, terutama untuk navigasi dan aksi, karena proxy residensial menambahkan latensi nyata per permintaan yang menumpuk seiring banyaknya permintaan yang dibuat oleh sebuah halaman. Ukur latensi tersebut melalui proxy dengan fitur pelacakan (tracing) yang diaktifkan, lalu tetapkan nilainya berdasarkan pengamatan Anda, daripada menaikkan batas waktu secara preventif untuk semua permintaan.

Kesimpulan

Pesan tersebut menyatakan bahwa tes melebihi 30 detik, dan itu hanyalah batasan waktu, bukan penjelasan. Ada sesuatu di dalamnya yang menunggu suatu kondisi yang tak pernah terpenuhi, dan dalam empat dari lima kasus, kondisi tersebut adalah selektor yang tidak cocok dengan apa pun atau elemen yang tak pernah menjadi dapat ditindaklanjuti.

Jadi, urutan langkah yang menghemat waktu adalah: baca pesan kesalahan secara lengkap, yang menyebutkan pemeriksaan kelayakan tindakan yang tertunda; buka jejak (trace), yang menunjukkan DOM pada saat kegagalan terjadi; pastikan lokator dapat diidentifikasi; dan baru setelah itu perhatikan angka tersebut. Mengaktifkan batas waktu (timeout) adalah solusi yang tepat untuk satu penyebab saja — operasi yang benar-benar memakan waktu lebih lama dari batas waktu — dan solusi yang salah untuk empat kasus lainnya, di mana hal itu hanya menunda kegagalan.

Jika Anda memang harus menetapkan batas waktu, lakukanlah secara terbatas. Per tes, per asersi, per fixture. Meningkatkan nilai default global untuk mengakomodasi satu unggahan yang lambat akan membuat setiap kegagalan dalam rangkaian tes menjadi lebih mahal, dan waktu CI adalah satu-satunya sumber daya yang tidak akan pernah kembali.