Saya sedang menjalankan audit QA di produksi untuk satu platform pemesanan rumah liburan milik klien. Bukan lewat unit test, tapi lewat browser sungguhan yang saya kendalikan dari sesi otomasi: buka halaman, isi form, klik tombol, lihat apa yang berubah. Selama pekerjaannya masih di area admin WordPress, semuanya lancar. Begitu pindah ke halaman depan, otomasinya berhenti bekerja sama sekali.
Perintah kliknya tidak error dan tidak salah sasaran. Dia cuma menggantung, lalu kehabisan waktu, dengan alasan yang selalu sama: waiting for stable. Halaman login begitu, halaman detail listing begitu, form tambah listing begitu, homepage juga begitu. Perintah screenshot ikut timeout di halaman yang sama. Jadi bukan cuma saya tidak bisa mengklik, saya juga tidak bisa memotret buktinya.
Yang menarik justru pembagiannya. Halaman admin stabil, halaman depan tidak. Satu situs, satu browser, satu sesi otomasi, dua perilaku yang berbeda total. Kalau kegagalan hanya menempel pada sebagian halaman dan sebagian itu punya satu ciri yang sama, ciri itulah tersangkanya.
Ciri yang sama itu animasi reveal
Halaman depan situs ini penuh animasi masuk. Section demi section dianimasikan saat masuk viewport, elemen bergeser dan memudar ke posisi akhirnya, dan gerakan itu terus dipicu ulang saat halaman di-scroll. Di mata pengunjung, ini terasa halus dan sengaja dibuat begitu. Di mata Playwright, ini masalah.
Sebelum benar-benar mengklik, Playwright menjalankan serangkaian pemeriksaan kelayakan pada elemen sasaran. Salah satunya bernama stable: kotak batas elemen harus tidak berubah di antara dua animation frame berturut-turut. Idenya masuk akal dan sebenarnya melindungi saya. Kalau sebuah tombol masih meluncur ke tempatnya, klik yang dikirim sekarang bisa mendarat di koordinat yang sudah ditinggalkan tombol itu sepersekian detik kemudian.
Masalahnya, pemeriksaan itu menunggu keadaan yang di halaman ini tidak pernah datang. Selalu ada sesuatu yang sedang bergerak, jadi kotak batasnya tidak pernah sama dua frame beruntun. Playwright menunggu dengan sabar sampai batas waktunya habis, lalu menyerah. Bukan bug di situsnya, dan bukan bug di Playwright. Ini dua asumsi yang saling bertabrakan: yang satu merancang halaman supaya selalu hidup, yang satu menolak bertindak sampai halamannya diam.
Perbaikannya: berhenti menunggu, kirim eventnya sendiri
Saya sempat kepikiran mematikan animasinya lewat CSS injeksi selama audit. Saya tidak jadi, karena yang sedang saya audit adalah produksi apa adanya, dan mengubah halamannya sebelum mengujinya berarti menguji halaman yang berbeda dari yang dilihat pengunjung.
Jalan yang saya ambil sebaliknya: lewati antrean stabilitasnya. Saya sudah tahu elemennya ada dan bisa berinteraksi. Yang saya butuhkan adalah bukti bahwa handler-nya jalan, bukan jaminan bahwa sebuah pointer fisik mendarat di permukaan yang diam. Jadi semua interaksinya saya kirim lewat evaluate, langsung ke DOM, tanpa melewati pemeriksaan kelayakan.
Bentuknya ada tiga, sesuai jenis elemennya.
Form: isi nilainya, lalu submit tanpa menyentuh tombolnya
Untuk login dan form panjang seperti tambah listing, saya isi fieldnya lewat evaluate lalu men-submit formnya langsung. Bisa dengan menekan Enter, bisa dengan memanggil requestSubmit().
const form = document.querySelector('form[data-login]');
form.querySelector('input[name="email"]').value = 'akun@contoh.test';
form.querySelector('input[name="password"]').value = 'rahasia';
form.requestSubmit();Selektor di atas cuma contoh bentuknya, sesuaikan dengan markup yang sedang kamu hadapi. Yang penting requestSubmit(), bukan submit(). submit() melewati validasi bawaan dan tidak menembakkan event submit, jadi handler apa pun yang dipasang situs pada event itu tidak akan pernah jalan dan kamu akan mengira formnya rusak. requestSubmit() berperilaku seperti tombol submit sungguhan.
Tombol dan kalender: kirim seluruh urutan MouseEvent
Untuk tombol dan date picker, el.click() saja sering tidak cukup. Panggilan itu hanya menembakkan event click, sementara banyak widget mulai bekerja jauh lebih awal, di pointerdown atau mousedown. Kalender tanggal termasuk yang begitu. Kalau yang dikirim cuma click, tidak ada yang terjadi dan kelihatannya seperti widget mati.
Jadi saya kirim seluruh urutannya, persis seperti yang dilakukan pointer sungguhan:
['pointerdown','mousedown','pointerup','mouseup','click'].forEach(t => el.dispatchEvent(new MouseEvent(t,{bubbles:true})));bubbles: true di situ bukan hiasan. Handler-nya sering dipasang secara delegasi di kontainer, bukan di elemen yang diklik, dan event yang tidak menggelembung tidak akan pernah sampai ke sana.
Ini sekalian membatalkan catatan lama saya sendiri. Di sesi-sesi sebelumnya saya menulis bahwa kalender di situs ini "butuh klik sungguhan" dan tidak bisa didorong secara sintetis. Ternyata tidak. Dia menerima klik sintetis yang menggelembung dengan baik. Yang dulu gagal kemungkinan besar bukan karena sintetisnya, tapi karena urutannya kurang lengkap.
Aksi yang ujungnya REST: potong sekalian sampai ke endpoint-nya
Beberapa aksi, misalnya menerbitkan sebuah listing, sebenarnya cuma pembungkus untuk satu panggilan REST. Untuk itu saya tidak repot dengan tombolnya sama sekali. Nonce-nya sudah ada di objek data yang di-localize tema ke halaman depan, jadi endpoint-nya bisa dipanggil langsung dari evaluate.
await fetch('/wp-json/app/v1/properties/123', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': window.appData.nonce,
},
body: JSON.stringify({ status: 'publish' }),
});Satu catatan yang menghemat waktu: objek data seperti itu belum tentu ada di setiap halaman. Di situs ini dia tersedia di halaman depan, tapi tidak selalu di halaman admin. Periksa dulu sebelum menganggapnya ada.
Ganti buktinya: DOM dan network, bukan screenshot
Karena screenshot ikut timeout di halaman yang sama, saya berhenti memakainya sebagai bukti dan beralih memeriksa keadaan DOM serta respons jaringan.
Ini terpaksa di awal, tapi belakangan saya sadar buktinya justru lebih kuat. Screenshot cuma membuktikan piksel di satu momen. Sebuah respons sukses dari endpoint ditambah baris baru yang benar-benar muncul di daftar membuktikan seluruh alurnya berjalan sampai ke basis data dan kembali lagi.
Perlu diingat juga bahwa tidak semua halaman butuh perlakuan ini. Halaman admin di situs yang sama sebagian besar stabil, dan di sana klik biasa jalan tanpa masalah. Yang sesekali bikin timeout cuma notice admin yang muncul dan menggeser layout, dan itu pun tertolong oleh form.requestSubmit() yang sama.
Yang tetap tidak bisa dibuktikan cara ini
Ada satu tempat di mana pendekatan ini mentok, dan saya rasa penting mencatatnya daripada pura-pura semuanya lulus uji.
Menu hamburger versi mobile saya dorong dengan event sintetis. Atribut aria-nya berubah persis seperti yang diharapkan, jadi handler-nya jelas jalan. Tapi panelnya ter-render dengan lebar nol, dan saya tidak bisa memastikan apakah itu bug sungguhan atau akibat saya menggerakkan widgetnya dengan cara yang tidak pernah dipakai manusia. Klik sungguhan lewat otomasi terhalang oleh ketidakstabilan animasi homepage tadi, jadi jalan itu tertutup juga.
Saya tidak menandainya lulus. Saya catat sebagai item yang belum terverifikasi, dengan permintaan supaya dites manual lewat tap di HP sungguhan. Otomasi yang melewati pemeriksaan kelayakan membeli kecepatan dengan menukar sebagian kepastian, dan tukar-tambah itu harus jujur ditulis di laporannya.
Yang saya bawa pulang
waiting for stablebukan tuduhan bahwa elemennya rusak. Itu Playwright bilang elemennya masih bergerak, dan di halaman beranimasi reveal, "masih bergerak" bisa berarti selamanya.- Kalau kegagalan cuma menempel di sebagian halaman, cari ciri yang dimiliki bersama oleh sebagian itu. Di sini cirinya animasi masuk, dan pembagian admin lawan halaman depan langsung menunjuk ke sana.
el.click()sendirian sering tidak cukup untuk widget yang mendengarkanpointerdownataumousedown. Kirim seluruh urutannya, dan pastikan menggelembung.- Pakai
requestSubmit(), bukansubmit(), kalau situsnya punya handler di event submit. - Kalau screenshot tidak bisa diandalkan, ganti buktinya dengan keadaan DOM dan respons jaringan. Sering kali itu bukti yang lebih baik sejak awal.
- Catatan lama tentang alat layak diuji ulang sesekali. Keyakinan saya bahwa kalendernya "butuh klik sungguhan" bertahan berminggu-minggu padahal salah, dan selama itu saya menghindari jalan yang sebenarnya terbuka.