jsdom membuktikan struktur kartu produk Budi benar, tetapi ada yang belum terjawab: apakah halaman sungguhan benar-benar menampilkannya? Apakah klik tombol “Bayar” membawa pembeli ke halaman yang tepat? Pertanyaan seperti ini menyentuh peramban sebagai kesatuan — HTML, CSS, jaringan, skrip, dan interaksi manusia sekaligus — dan tidak bisa dijawab oleh tiruan DOM di dalam Node.

Lapisan E2E (end-to-end) menjawabnya dengan otomasi browser: program yang menjalankan browser sungguhan, membuka aplikasi, berperilaku seperti pengguna — mengetik, mengklik, menggulir — lalu mengklaim apa yang tampak. Framework yang paling banyak dipakai di ekosistem JavaScript untuk peran ini adalah Playwright, dan chapter ini memperkenalkan konsep-konsepnya sebagai peta untuk saat kita benar-benar membutuhkannya.

Playwright dalam Sekilas Pandang #

Playwright adalah framework pengujian E2E yang dikembangkan Microsoft, dipakai dari Node.js. Tiga karakteristiknya menentukan cara kerjanya:

  • Browser sungguhan. Playwright mengendalikan Chromium, Firefox, dan WebKit — mesin yang sama dengan Chrome, Firefox, dan Safari. Uji berjalan di aplikasi yang benar-benar menghitung layout, memuat aset, dan mengeksekusi skrip halaman.
  • Headless atau headed. Secara bawaan browser dijalankan headless (tanpa jendela tampilan) agar cepat dan cocok untuk server CI; opsi headed membuka jendela browser sungguhan saat kita ingin melihat ujinya berjalan.
  • Runner dan asersi satu paket. Berbeda dari node:test yang berpasangan dengan node:assert, Playwright membawa test runner sendiri beserta asersi yang menunggu — lebih lanjut di bawah.

Tiga Konsep Inti: browser, page, locator #

Hampir semua skrip Playwright dibangun dari tiga konsep. browser adalah instans aplikasi browser; page adalah satu tab di dalamnya; dan locator adalah resep pencari elemen — bukan elemen itu sendiri. Locator dievaluasi setiap kali dipakai, sehingga bila halaman berubah di tengah uji, pencarian dilakukan ulang pada kondisi terbaru. Ilustrasi berikut merangkai ketiganya untuk skenario pencarian produk:

js ilustrasi-cari-produk.spec.js — tidak dijalankan
import { test, expect } from '@playwright/test';

test('pencarian produk menampilkan hasil', async ({ page }) => {
  await page.goto('https://toko.example.com');

  const kolomCari = page.getByRole('textbox', { name: 'Cari produk' });
  await kolomCari.fill('algoritma');
  await page.getByRole('button', { name: 'Cari' }).click();

  const kartu = page.locator('.kartu-produk');
  await expect(kartu).toHaveCount(3);
  await expect(kartu.first()).toContainText('Buku Algoritma');
});

Mari kita telaah pola pentingnya:

  1. import { test, expect } from '@playwright/test': test di sini milik runner Playwright — bukan node:test — dan expect adalah asersinya. Keduanya datang dari satu paket yang juga menyediakan perintah menjalankannya.
  2. async ({ page }) => {...}: parameter berupa objek fixture. Playwright menyiapkan page baru untuk setiap uji — tab segar di browser terpisah — lalu membereskannya setelah uji selesai. Kita tidak pernah membuat dan menutup browser secara manual di tingkat ini.
  3. page.getByRole('textbox', { name: 'Cari produk' }): locator berbasis role aksesibilitas. Pencarian lewat peran dan nama yang dapat diakses sekaligus menegaskan bahwa halaman bisa dipakai pembaca layar — praktik yang sangat dianjurkan dibanding mengandalkan class CSS yang sering berubah.
  4. await kolomCari.fill('algoritma'): setiap aksi Playwright bersifat auto-waiting: fill menunggu elemen muncul, aktif, dan bisa diisi sebelum mengetik. Kita tidak menulis penungguan manual seperti pada banyak framework lama.
  5. await expect(kartu).toHaveCount(3): asersi Playwright juga menunggu — ia memeriksa ulang kondisi berkali-kali sampai benar atau habis batas waktu. Perilaku ini wajar untuk E2E karena hasil pencarian butuh waktu bervariasi untuk muncul; asersi node:assert yang memeriksa satu kali begitu saja akan sering gagal keliru.

Kombinasi fixture page, locator, dan asersi yang menunggu inilah yang membuat skrip E2E terbaca seperti cerita pengguna: buka, isi, klik, lalu pastikan hasilnya.

Menata Berkas dan Perintah Utama #

Dalam proyek nyata, Playwright dipasang sebagai dependensi pengembangan, dan berkas ujinya lazim dinamai *.spec.js di dalam folder tests/. Perintah-perintah yang dipakai sehari-hari:

bash
npm install --save-dev @playwright/test   # pasang framework
npx playwright install                    # unduh binary browser
npx playwright test                       # jalankan seluruh uji headless
npx playwright test --headed              # jalankan dengan jendela terlihat
npx playwright test uji-cari.spec.js      # jalankan satu berkas
npx playwright show-report                # buka laporan hasil uji

Runner Playwright menjalankan berkas-berkas uji secara paralel di beberapa worker, sehingga rangkaian E2E yang panjang pun selesai dalam waktu wajar. Laporan HTML bawaan menampilkan setiap langkah uji beserta tangkapan layar saat kegagalan — bantuan yang sangat berharga karena kegagalan E2E jarang sesederhana 111000 !== 110000.

Kapan Uji E2E Ditulis #

Uji E2E adalah lapisan paling mahal di piramide: setiap uji menjalankan browser, memuat halaman, dan bisa gagal karena alasan yang berada jauh dari logika yang sedang diuji. Karena itu pakai secara hemat dan terarah:

  • Jalur utama (smoke test). Satu-dua skenario yang harus selalu benar — cari produk, tambahkan ke keranjang, selesaikan pembayaran. Bila jalur ini putus, bisnis ikut berhenti.
  • Perilaku lintas halaman. Hal-hal yang mustahil diuji dengan unit maupun jsdom: redirect, form multi-langkah, data yang mengalir antar halaman.
  • Perubahan besar sebelum rilis. Uji E2E yang stabil adalah jaring pengaman terakhir.

Sebaliknya, jangan gunakan E2E untuk menguji rumus diskon atau format rupiah — pekerjaan itu sudah tuntas di uji unit yang berjalan ribuan kali lebih cepat. Selector yang rapuh juga wajib dijaga: memakai getByRole dan atribut data khusus uji membuat skrip tidak mudah patah setiap desain berubah.

Dengan ketiga lapisan piramide lengkap di peta — unit, integration, E2E — satu pertanyaan praktis tersisa untuk chapter berikutnya: seberapa banyak kode kita yang sebenarnya sudah disentuh uji?