Programming

Playwright: Testing Multi-Browser Tanpa Drama

M
MUGHU
37 menit baca
Playwright: Testing Multi-Browser Tanpa Drama
Daftar isi

Bayangkan ini: lo baru aja deploy fitur baru ke production. Semua unit test hijau. Tapi begitu user mulai pakai, bug muncul di Safari padahal di Chrome lancar-lancar aja. Atau lebih nyebelin lagi — test E2E lo tiba-tiba gagal di CI dengan error TimeoutError, padahal di laptop sendiri jalan mulus. Kalau Teman-Teman pernah ngalamin hal kayak gini, percaya deh, lo nggak sendirian.

Di sinilah Playwright masuk sebagai game-changer. Framework end-to-end testing buatan Microsoft ini dirancang dari bawah untuk menangani masalah-masalah klasik yang bikin test automation terasa kayak perang parit: flaky test, setup browser yang ribet, cross-browser testing yang bikin pusing, dan debugging yang bikin ngelus dada.

Playwright adalah framework open-source untuk browser automation dan end-to-end testing yang dikembangkan Microsoft, dirilis pertama kali pada 31 Januari 2020. Framework ini memungkinkan lo menulis test sekali dan menjalankannya di Chromium, Firefox, dan WebKit — tanpa konfigurasi tambahan.

Artikel ini bakal ngebahas Playwright dari nol sampai lo bisa nulis test produksi yang stabil. Saya akan ajak Teman-Teman ngeliat gimana cara install, nulis test pertama, debugging, sampai integrasi CI/CD — lengkap dengan studi kasus nyata dan perbandingan sama tools lain. Anggap aja ini obrolan santai sambil ngopi, tapi isinya daging semua.


Masalah yang Diselesaikan Playwright

Sebelum ngomongin kode, penting buat ngerti dulu: kenapa sih Playwright itu ada? Apa masalah yang sebenarnya diselesaikan?

Pain Point #1: Flaky Test yang Bikin Trust Issue

Ini musuh bersama. Test kadang hijau, kadang merah, padahal kode aplikasi nggak berubah. Saya dulu pernah ngelola suite Selenium dengan 300+ test; setiap kali CI jalan, 40-50 test gagal. Setelah dicek ulang, 35 di antaranya sukses. Masalahnya klasik: timing. Elemen belum muncul tapi test udah lanjut, atau halaman belum selesai render tapi assertion udah dijalankan.

Pain Point #2: Cross-Browser Testing yang Ribet

Mau ngetes di Chrome oke, Firefox oke, tapi Safari? Mulai deh drama. Masing-masing browser butuh driver sendiri, konfigurasi sendiri, dan kadang perilaku yang beda-beda.

Pain Point #3: Debugging yang Kayak Detektif Amatir

Test gagal di CI. Cuma dikasih stack trace. Lo harus mereproduksi ulang secara manual. Kalau beruntung, bisa ke-reproduce. Kalau nggak? Selamat menebak-nebak.

Kenapa Ini Penting Banget

Di era continuous delivery, kecepatan feedback adalah segalanya. Kalau test automation lo nggak bisa dipercaya, tim bakal mulai mengabaikan hasil test. Dan begitu test diabaikan, value dari automation testing lo jadi nol besar. Ini bukan cuma soal teknis — ini soal ROI dari investasi testing tim lo.


Apa Itu Playwright? Arsitektur dan Konsep Inti

Secara teknis, Playwright adalah browser automation library yang berkomunikasi langsung dengan browser melalui protokol level rendah: Chrome DevTools Protocol (CDP) untuk Chromium, WebDriver BiDi untuk Firefox, dan protokol internal untuk WebKit. Ini yang bikin Playwright bisa ngontrol browser dengan presisi tinggi — termasuk network interception, DOM inspection, dan JavaScript execution.

Model Tiga Lapisan: Browser, Context, Page

Biar ngerti cara kerja Playwright, Teman-Teman perlu paham tiga lapisan ini:

Lapisan

Peran

Analogi Sederhana

Browser

Instance browser utama (Chromium, Firefox, WebKit)

Aplikasi Chrome yang lo klik dari desktop

Context

Sesi terisolasi di dalam browser, seperti incognito window

Satu jendela incognito dengan cookies & storage sendiri

Page

Tab tempat lo berinteraksi dengan halaman web

Satu tab di dalam jendela incognito tadi

Yang keren dari arsitektur ini: setiap context punya cookies, localStorage, dan session data sendiri. Jadi satu test nggak akan "bocor" ke test lain. Inilah fondasi kenapa Playwright bisa jalanin test paralel dengan aman — tanpa state sharing yang bikin flaky.

Fitur Unggulan dalam Satu Toolbox

Playwright nggak cuma test runner. Dia ekosistem lengkap yang mencakup:

  • Auto-waiting: Playwright otomatis nunggu elemen siap sebelum berinteraksi. Nggak perlu sleep(3000) atau waitForSelector manual.

  • Web-first assertions: Assertion kayak toHaveText atau toBeVisible bakal retry otomatis sampai kondisi terpenuhi atau timeout.

  • Test isolation: Tiap test dapat browser context baru — setara dengan profil browser yang fresh.

  • Parallel execution: Test jalan paralel secara default di semua browser yang dikonfigurasi.

  • Built-in tools: Codegen, Trace Viewer, HTML Report, dan VS Code extension — semuanya udah include, nggak perlu plugin pihak ketiga.


Playwright vs Selenium vs Cypress

Kalau Teman-Teman udah familiar dengan Selenium atau Cypress, wajar banget nanya: "Ngapain gue pindah? Emang Playwright lebih baik?" Jawabannya: tergantung konteks. Tapi secara umum, Playwright unggul di hampir semua aspek untuk pengembangan web modern.

Tabel Perbandingan Lengkap

Aspek

Playwright

Selenium

Cypress

Browser Support

Chromium, Firefox, WebKit

Semua browser utama (via driver)

Chrome, Edge, Firefox (terbatas)

Bahasa Pemrograman

JS/TS, Python, Java, .NET

JS, Python, Java, C#, Ruby, dll

JavaScript/TypeScript only

Auto-Waiting

✅ Bawaan

❌ Manual

✅ Bawaan

Parallel Execution

✅ Bawaan, tanpa setup

Manual via Grid

✅ Bawaan (terbatas)

Multi-Tab & iFrame

✅ Native support

✅ Support

❌ Terbatas

Mobile Emulation

✅ Bawaan

Perlu setup tambahan

❌ Terbatas

Network Interception

✅ Sangat powerful

Terbatas (Selenium 4+)

✅ Support

Screenshot & Video

✅ Bawaan

Perlu library pihak ketiga

✅ Bawaan

Debugging Tools

Trace Viewer, Inspector, UI Mode

Mengandalkan browser tools

Built-in Test Runner UI

Kecepatan Eksekusi

Sangat cepat

Lebih lambat

Cepat (in-browser)

Learning Curve

Menengah

Menengah sampai curam

Mudah

CI/CD Integration

Simpel, tinggal install

Butuh Grid setup

Straightforward

Dikembangkan Oleh

Microsoft

Open-source community

Cypress.io

Rekomendasi Berdasarkan Use Case

  • Gunakan Playwright kalau: Lo butuh cross-browser testing serius, test paralel skala besar, atau aplikasi lo punya banyak dynamic content dan network request.

  • Pertimbangkan Selenium kalau: Lo punya legacy system yang udah heavily invested di Selenium ecosystem, atau butuh support bahasa yang belum didukung Playwright (Ruby, PHP, etc.).

  • Pilih Cypress kalau: Lo cuma testing di Chrome-based browser, tim lo udah nyaman dengan JavaScript, dan lo nggak butuh multi-tab atau network interception yang kompleks.


Prasyarat Instalasi

Sebelum mulai nulis kode, pastikan environment lo udah siap. Yang dibutuhkan:

  • Node.js versi 18 ke atas — Playwright berjalan di atas Node.js runtime. Cek dengan node -v di terminal.

  • npm, yarn, atau pnpm — Package manager untuk install Playwright dan dependency-nya.

  • Visual Studio Code (direkomendasikan) — Meskipun bisa pakai editor apa aja, VS Code punya ekstensi resmi Playwright yang bikin workflow jauh lebih lancar.

  • Koneksi internet — Karena Playwright akan download browser binary saat instalasi.

Kenapa VS Code? Karena ekstensi Playwright Test for VS Code udah didownload lebih dari 1,6 juta kali dan ngasih fitur kayak Test Explorer, inline test results, dan "Fix with AI" button. Worth it banget.


Step 1: Instalasi Playwright

Buka terminal di folder proyek lo, lalu jalankan:

BASH
npm init playwright@latest

Command ini akan memulai interactive setup. Lo bakal ditanyain beberapa hal:

  1. TypeScript atau JavaScript — Default TypeScript. Saya rekomen TypeScript biar dapet type safety, apalagi kalau proyeknya udah gede.

  2. Lokasi folder test — Default tests/, bisa juga e2e/ kalau tests/ udah ada isinya.

  3. GitHub Actions workflow — Pilih true kalau lo rencana integrasi CI/CD. Ini akan generate .github/workflows/playwright.yml otomatis.

  4. Install Playwright browsers — Pastiin pilih true. Ini yang akan download Chromium, Firefox, dan WebKit binary.

Setelah instalasi selesai, struktur proyek lo bakal kayak gini:

CODE
playwright.config.ts
package.json
package-lock.json
tests/
  example.spec.ts

Kenapa Langkah Ini Penting?

Instalasi via npm init playwright@latest bukan cuma install package. Dia juga:

  • Download browser binary (Chromium, Firefox, WebKit) — ini sekitar ~400MB per browser, jadi pastiin koneksi stabil.

  • Generate playwright.config.ts yang udah dikonfigurasi dengan default yang masuk akal.

  • Bikin sample test biar lo bisa langsung verifikasi bahwa setup lo jalan.

Expected Output

CODE
Initializing NPM project (npm init -y)…
Installing Playwright Test (npm install --save-dev @playwright/test)…
Downloading browsers (Chromium, Firefox, WebKit)…

Troubleshooting Instalasi

  • Error: browser binaries not found — Jalankan npx playwright install secara manual.

  • Error permission di Linux — Coba jalankan npx playwright install --with-deps untuk install system dependencies.

  • Instalasi lambat — Browser binary sekitar 400-500MB, jadi pastikan koneksi internet stabil. Kalau di Indonesia, pertimbangkan untuk pakai koneksi yang nggak gampang timeout.


Step 2: Memahami Struktur Proyek Playwright

Sebelum ngoding, penting buat ngerti dulu apa fungsi masing-masing file dan folder.

playwright.config.ts — Pusat Kendali

Ini file paling penting. Semua konfigurasi global ada di sini: browser apa yang dipakai, timeout, retry, parallel workers, dan reporter. Contoh isinya:

TYPESCRIPT
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  timeout: 30000,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 2 : 4,
  reporter: [['html'], ['list']],
  use: {
    baseURL: 'https://example.com',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
  ],
});

tests/ Folder — Tempat Test Lo Tinggal

Playwright auto-scan folder ini buat nyari file dengan pola *.spec.ts atau *.test.ts. Biasanya test dikelompokkan berdasarkan fitur atau modul.

package.json — Metadata dan Script

Di sini lo bisa nambahin script shortcut:

JSON
{
  "scripts": {
    "test": "npx playwright test",
    "test:headed": "npx playwright test --headed",
    "test:debug": "npx playwright test --debug",
    "test:ui": "npx playwright test --ui"
  }
}

node_modules/ — Dependency

Jangan diutak-atik. Ini tempat Playwright dan browser binary disimpan setelah instalasi.


Step 3: Menulis Test Pertama Lo

Sekarang praktik langsung. Buka folder tests/, bikin file baru pertama.spec.ts, lalu tulis kode ini:

TYPESCRIPT
import { test, expect } from '@playwright/test';

test('halaman utama punya judul yang benar', async ({ page }) => {
  // 1. Buka halaman
  await page.goto('https://example.com');

  // 2. Verifikasi judul halaman
  await expect(page).toHaveTitle(/Example Domain/);

  // 3. Cari heading dan verifikasi teksnya
  const heading = page.getByRole('heading', { name: 'Example Domain' });
  await expect(heading).toBeVisible();

  // 4. Klik link dan verifikasi navigasi
  await page.getByRole('link', { name: 'More information' }).click();
  await expect(page).toHaveURL(/iana.org/);
});

Breakdown: Apa yang Terjadi di Setiap Baris?

  1. import { test, expect } from '@playwright/test' — Mengimpor dua fungsi inti Playwright. test untuk mendefinisikan test case, expect untuk assertion.

  2. test('nama test', async ({ page }) => { ... }) — Membuat test case. page adalah fixture bawaan yang merepresentasikan tab browser. Playwright otomatis bikin page baru untuk setiap test — jadi nggak perlu setup manual.

  3. page.goto('https://example.com') — Navigasi ke URL. Playwright otomatis nunggu halaman selesai load sebelum lanjut.

  4. expect(page).toHaveTitle(/Example Domain/) — Assertion dengan regex. Cek judul halaman mengandung "Example Domain".

  5. page.getByRole('heading', { name: 'Example Domain' }) — Locator berbasis peran aksesibilitas. Ini lebih stabil dibanding CSS selector.

  6. expect(heading).toBeVisible() — Verifikasi elemen terlihat. Ini bakal retry otomatis sampai elemen visible atau timeout.

  7. .click() dan toHaveURL() — Interaksi klik dan verifikasi URL.

Kenapa Tiap Langkah Ini Penting?

  • Navigasi: Tanpa verifikasi judul, lo nggak tahu apakah halaman yang dituju beneran ke-load atau malah kena redirect/error.

  • Visibility check: Elemen bisa aja ada di DOM tapi hidden (misal karena CSS display: none). toBeVisible() memastikan elemen beneran bisa dilihat user.

  • Assertion setelah aksi: Klik link tanpa verifikasi navigasi itu testing setengah hati. Lo harus pastiin aksi user menghasilkan outcome yang diharapkan.

Menjalankan Test

BASH
npx playwright test tests/pertama.spec.ts

Expected Output

CODE
Running 3 tests using 3 workers

  ✓  1 [chromium] › tests/pertama.spec.ts:3:1 › halaman utama punya judul yang benar (1.2s)
  ✓  2 [firefox] › tests/pertama.spec.ts:3:1 › halaman utama punya judul yang benar (1.4s)
  ✓  3 [webkit] › tests/pertama.spec.ts:3:1 › halaman utama punya judul yang benar (1.1s)

  3 passed (5s)

Test lo jalan di tiga browser sekaligus dalam waktu sekitar 5 detik. Ini paralel execution bawaan Playwright — nggak perlu konfigurasi tambahan.


Step 4: Memahami Locator — Kunci Test yang Stabil

Kalau ada satu skill yang paling ngaruh ke kestabilan test lo, itu adalah memilih locator yang tepat. Locator adalah cara lo "menunjuk" elemen di halaman. Playwright nyediain beberapa strategi locator, dan pilihan lo di sini bisa bikin test lo hidup bertahun-tahun atau mati dalam seminggu.

Locator Bawaan yang Direkomendasikan

Locator

Contoh

Kapan Dipakai

getByRole()

page.getByRole('button', { name: 'Login' })

Selalu — ini prioritas utama. Paling tahan banting terhadap perubahan DOM

getByLabel()

page.getByLabel('Email')

Form input yang punya <label> terkait

getByPlaceholder()

page.getByPlaceholder('Masukkan password')

Input dengan placeholder

getByText()

page.getByText('Selamat datang')

Elemen dengan teks spesifik yang unik

getByTestId()

page.getByTestId('submit-btn')

Ketika lo bisa nambahin atribut data-testid di komponen

locator()

page.locator('.btn-primary')

CSS atau XPath — gunakan sebagai opsi terakhir

Kenapa getByRole() Itu Raja?

Karena dia berbasis accessibility tree, bukan struktur DOM. User asli nggak peduli apa class CSS tombol lo — mereka peduli bahwa ada tombol bernama "Login" yang bisa diklik. Kalau lo ganti class CSS dari .btn-login jadi .btn-primary, test getByRole tetap jalan. Kalau lo pakai .btn-login, test langsung merah.

Contoh nyata dari pengalaman saya: dulu pakai Selenium dengan CSS selector, tiap kali frontend refactor class naming, 30% test merah. Setelah migrasi ke Playwright dengan semantic locator, angka itu turun drastis jadi di bawah 5%.

Teknik Chaining dan Filtering

Kadang satu locator match lebih dari satu elemen. Solusinya:

TYPESCRIPT
// Chaining: cari dalam scope tertentu dulu
const dialog = page.getByRole('dialog');
await dialog.getByRole('button', { name: 'Simpan' }).click();

// Filtering: pilih yang mengandung teks tertentu
const row = page.locator('tr').filter({ hasText: 'Ahmad' });
await row.getByRole('button', { name: 'Edit' }).click();

Common Error: Strict Mode Violation

CODE
Error: locator.click: Error: strict mode violation:
getByText('Lanjutkan') resolved to 2 elements

Playwright punya strict mode: satu locator harus resolve ke tepat satu elemen. Kalau nemu dua atau lebih, dia nolak. Ini sebenarnya fitur, bukan bug — memaksa lo untuk spesifik. Solusinya: pakai chaining atau .first() / .nth(0).


Step 5: Assertions — Memastikan Aplikasi Berperilaku Benar

Assertion adalah jantung dari testing. Tanpa assertion, test cuma sekedar "jalanin aksi" tanpa verifikasi hasil. Playwright punya web-first assertions yang secara otomatis retry sampai kondisi terpenuhi.

Assertions Paling Sering Dipakai

TYPESCRIPT
// Verifikasi halaman
await expect(page).toHaveTitle('Dashboard - MyApp');
await expect(page).toHaveURL(/dashboard/);

// Verifikasi elemen
await expect(page.getByText('Login berhasil')).toBeVisible();
await expect(page.getByRole('textbox', { name: 'Email' })).toBeEnabled();
await expect(page.getByRole('checkbox')).toBeChecked();

// Verifikasi konten
await expect(page.getByTestId('user-count')).toHaveText('42');
await expect(page.getByTestId('price')).toContainText('Rp');

// Verifikasi screenshot (visual regression)
await expect(page).toHaveScreenshot('homepage.png');

Auto-Retry: Kenapa Ini Game-Changer

Setiap assertion di atas bakal retry otomatis selama timeout (default 5 detik untuk assertion). Jadi kalau lo nulis:

TYPESCRIPT
await page.getByRole('button', { name: 'Simpan' }).click();
await expect(page.getByText('Data tersimpan')).toBeVisible();

Playwright akan:

  1. Klik tombol "Simpan"

  2. Mulai ngecek setiap beberapa milidetik: "Apakah teks 'Data tersimpan' udah muncul?"

  3. Begitu muncul, assertion sukses dan test lanjut

  4. Kalau setelah 5 detik belum muncul juga, test gagal

Nggak ada sleep(2000) atau waitForSelector manual. Ini yang bikin test Playwright jauh lebih cepat dan stabil dibanding pendekatan tradisional.


Step 6: Menjalankan Test di Berbagai Browser dan Mode

Salah satu kekuatan terbesar Playwright adalah cross-browser testing tanpa effort tambahan.

Konfigurasi Multi-Browser

Di playwright.config.ts:

TYPESCRIPT
projects: [
  { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
  { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
  { name: 'webkit', use: { ...devices['Desktop Safari'] } },
  // Mobile
  { name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
  { name: 'mobile-safari', use: { ...devices['iPhone 13'] } },
],

Menjalankan di Browser Spesifik

BASH
# Semua browser (default)
npx playwright test

# Browser tertentu
npx playwright test --project=chromium
npx playwright test --project=firefox

# Headed mode (lihat browser berjalan)
npx playwright test --headed

# Debug mode (step by step)
npx playwright test --debug

Headless vs Headed

  • Headless (default): Browser jalan di background tanpa GUI. Lebih cepat, cocok untuk CI/CD.

  • Headed: Browser muncul visible. Cocok buat development dan debugging.

Kenapa Cross-Browser Testing Itu Kritis?

Di Indonesia, pangsa browser cukup beragam. Chrome memang dominan, tapi Safari di kalangan pengguna iPhone/iPad cukup signifikan — terutama untuk aplikasi B2C, e-commerce, dan layanan publik. Menurut data statistik global, Safari bisa mencapai 18-25% traffic mobile di segmen tertentu. Jadi kalau lo cuma testing di Chrome, lo basically nge-blind 1 dari 5 user lo.


Step 7: Debugging — Trace Viewer, Inspector, dan UI Mode

Kalau test gagal, Playwright ngasih lo tiga senjata utama buat debug.

Playwright Inspector (--debug)

BASH
npx playwright test --debug

Ini membuka browser headed + Playwright Inspector. Lo bisa:

  • Step through satu per satu — lihat apa yang terjadi di setiap langkah

  • Explore locator — klik elemen di halaman dan Inspector kasih tahu locator yang direkomendasikan

  • Lihat log — console output, network request, dan error semua tercapture

Trace Viewer

Trace adalah rekaman lengkap dari test run lo. Isinya:

  • DOM snapshot di setiap langkah

  • Timeline aksi (klik, ketik, navigasi)

  • Network requests dan responses

  • Console logs dan error

  • Screenshots

Aktifkan trace di config:

TYPESCRIPT
use: {
  trace: 'on-first-retry', // rekam trace saat retry pertama
}

Setelah test selesai, buka trace:

BASH
npx playwright show-trace trace.zip

Dulu saya pernah debugging test yang cuma gagal di CI, nggak bisa direproduksi lokal. Trace Viewer nyelamatin saya — ternyata di CI ada network request ke API pihak ketiga yang timeout, dan UI nampilin state berbeda setelah timeout itu. Tanpa trace, hampir mustahil buat detect.

UI Mode

BASH
npx playwright test --ui

Ini membuka antarmuka visual di mana lo bisa:

  • Browse dan run test dengan klik

  • Filter test berdasarkan status, tag, atau file

  • Lihat step-by-step execution dengan time-travel

  • Inspect DOM snapshot di setiap langkah

Mirip kayak DevTools tapi khusus untuk test. Cocok banget buat development sehari-hari.

Copy as Prompt — AI-Assisted Debugging

Fitur keren yang mungkin belum banyak yang tahu: di HTML Report, Trace Viewer, atau UI Mode, ada tombol Copy as Prompt. Lo bisa copy konteks failure dan share ke AI tool (seperti GitHub Copilot atau ChatGPT) untuk bantu analisis. Ini yang disebut AI-assisted debugging, dan ini udah jadi workflow standar di banyak tim.


Step 8: Merekam Test dengan Codegen

Nulis test dari nol itu kadang lambat, apalagi kalau lo belum familiar sama struktur DOM aplikasi. Playwright punya solusi: Codegen — test recorder yang generate kode sambil lo klik-klik di browser.

Cara Pakai Codegen

BASH
npx playwright codegen https://aplikasi-lo.com

Browser akan terbuka, dan setiap aksi yang lo lakukan (klik, ketik, navigasi) akan otomatis digenerate jadi kode Playwright di panel samping. Lo tinggal copy-paste dan rapikan.

Codegen via VS Code Extension

Kalau lo pakai VS Code dengan Playwright extension, tinggal klik Record New di sidebar Testing. Nggak perlu command line.

Kapan Codegen Berguna (dan Kapan Enggak)

Codegen bagus banget buat:

  • Bootstraping test — generate struktur awal, lalu lo refine manual

  • Eksplorasi locator — lihat locator apa yang dipilih Playwright untuk tiap elemen

  • Belajar — buat yang baru kenal Playwright, ini cara cepat ngeliat gimana API dipakai

Tapi jangan jadikan Codegen sebagai satu-satunya cara nulis test. Hasil generate biasanya butuh refactoring: ganti selector hardcoded jadi semantic locator, tambahin assertion yang meaningful, dan rapikan struktur test.


Step 9: Mobile Emulation dan Pengujian Responsif

Aplikasi web modern harus jalan di mobile. Playwright bikin pengujian mobile jadi gampang dengan built-in device emulation.

Emulasi Device

TYPESCRIPT
import { test, expect, devices } from '@playwright/test';

const iPhone = devices['iPhone 13'];

test.use({
  ...iPhone, // viewport, user agent, touch, pixel ratio — semua diset otomatis
});

test('tampilan mobile berfungsi', async ({ page }) => {
  await page.goto('https://aplikasi-lo.com');
  await expect(page.getByRole('button', { name: 'Menu' })).toBeVisible();
});

Playwright udah punya 30+ device preset, dari iPhone SE sampai Pixel 7. Tinggal pilih.

Geolocation dan Permissions

TYPESCRIPT
test.use({
  geolocation: { latitude: -6.2088, longitude: 106.8456 }, // Jakarta
  permissions: ['geolocation'],
  locale: 'id-ID',
  timezoneId: 'Asia/Jakarta',
});

test('menampilkan konten berdasarkan lokasi Jakarta', async ({ page }) => {
  await page.goto('https://aplikasi-lo.com/nearby');
  await expect(page.getByText('Jakarta Pusat')).toBeVisible();
});

Ini krusial buat aplikasi yang behavior-nya bergantung lokasi — misal e-commerce yang nampilin ongkir, layanan ride-hailing, atau food delivery. Untuk developer Indonesia yang aplikasinya melayani berbagai kota kayak Jakarta, Surabaya, Bandung, dan Medan, fitur ini sangat membantu.


Step 10: Network Interception dan API Mocking

Salah satu fitur Playwright yang paling powerful — dan sering underutilized — adalah network interception. Lo bisa intercept request, mock response, atau bahkan block request sama sekali.

Mocking API Response

Bayangkan lo lagi testing halaman daftar user. API production lo lagi down, atau datanya berubah-ubah. Solusinya:

TYPESCRIPT
test('menampilkan daftar user dengan data mock', async ({ page }) => {
  // Intercept request ke API dan ganti response-nya
  await page.route('**/api/users', route => {
    route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify([
        { id: 1, name: 'Budi', role: 'Admin' },
        { id: 2, name: 'Sari', role: 'User' },
      ]),
    });
  });

  await page.goto('https://aplikasi-lo.com/users');
  await expect(page.getByText('Budi')).toBeVisible();
  await expect(page.getByText('Sari')).toBeVisible();
});

Testing Error State

Gimana cara ngetes tampilan error tanpa bikin API beneran error? Gampang:

TYPESCRIPT
test('menampilkan pesan error saat API gagal', async ({ page }) => {
  await page.route('**/api/users', route => {
    route.fulfill({
      status: 500,
      body: JSON.stringify({ error: 'Internal Server Error' }),
    });
  });

  await page.goto('https://aplikasi-lo.com/users');
  await expect(page.getByText('Gagal memuat data')).toBeVisible();
});

Blocking Third-Party Requests

Kalau aplikasi lo loading analytics, tracking pixel, atau widget pihak ketiga yang bikin test lambat:

TYPESCRIPT
test('load tanpa gangguan pihak ketiga', async ({ page }) => {
  await page.route('**/*google-analytics*', route => route.abort());
  await page.route('**/*facebook.com/tr*', route => route.abort());

  await page.goto('https://aplikasi-lo.com');
  // Test berjalan lebih cepat tanpa nunggu third-party script
});

Saya personally selalu nge-block analytics dan tracking di test environment. Ini bisa motong waktu eksekusi test sampai 30-40%, terutama kalau aplikasi lo banyak integrasi pihak ketiga.


Step 11: Authentication dan Session Management

Login via UI di setiap test itu buang-buang waktu — dan bikin test lo fragile karena bergantung sama flow login yang stabil. Playwright punya solusi elegan: storageState.

Pattern: Setup + Reuse

Setup: Login sekali, simpan session:

TYPESCRIPT
// tests/auth.setup.ts
import { test as setup } from '@playwright/test';

setup('login dan simpan session', async ({ page }) => {
  await page.goto('https://aplikasi-lo.com/login');
  await page.getByLabel('Email').fill('[email protected]');
  await page.getByLabel('Password').fill('password123');
  await page.getByRole('button', { name: 'Masuk' }).click();
  await page.waitForURL('/dashboard');

  // Simpan state ke file
  await page.context().storageState({ path: '.auth/user.json' });
});

Reuse: Pake session yang udah disimpan:

TYPESCRIPT
// playwright.config.ts
projects: [
  { name: 'setup', testMatch: /auth\.setup\.ts/ },
  {
    name: 'chromium',
    use: { storageState: '.auth/user.json' },
    dependencies: ['setup'],
  },
],

Sekarang semua test di project chromium akan mulai dengan state logged-in — tanpa perlu login via UI. Ini yang bikin suite 200+ test tetap jalan dalam hitungan menit, bukan puluhan menit.

Multi-User Scenario

Untuk test yang butuh beberapa role sekaligus (admin vs user biasa):

TYPESCRIPT
test('admin bisa hapus data, user biasa tidak', async ({ browser }) => {
  const adminCtx = await browser.newContext({
    storageState: '.auth/admin.json',
  });
  const userCtx = await browser.newContext({
    storageState: '.auth/user.json',
  });

  const adminPage = await adminCtx.newPage();
  const userPage = await userCtx.newPage();

  await adminPage.goto('https://aplikasi-lo.com/admin');
  await expect(adminPage.getByText('Panel Admin')).toBeVisible();

  await userPage.goto('https://aplikasi-lo.com/admin');
  await expect(userPage.getByText('Akses ditolak')).toBeVisible();
});

Step 12: Integrasi CI/CD dengan GitHub Actions

Test automation tanpa CI/CD itu kayak masak nasi tanpa rice cooker — masih bisa, tapi ribet dan nggak efisien. Playwright udah punya workflow GitHub Actions siap pakai.

Konfigurasi .github/workflows/playwright.yml

YAML
name: Playwright Tests
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
jobs:
  test:
    timeout-minutes: 15
    runs-on: ubuntu-latest
    strategy:
      matrix:
        browser: [chromium, firefox, webkit]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - name: Install dependencies
        run: npm ci
      - name: Install Playwright browsers
        run: npx playwright install --with-deps
      - name: Run Playwright tests
        run: npx playwright test --project=${{ matrix.browser }}
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report-${{ matrix.browser }}
          path: playwright-report/

Kenapa Setup Ini Penting

  • Matrix strategy: Lo bisa jalanin test di tiga browser paralel di CI.

  • --with-deps: Install system dependencies yang dibutuhkan browser di Ubuntu (seperti libgbm, libnss3, dll).

  • if: always(): Upload report bahkan kalau test gagal — biar lo tetap bisa debug.

Konfigurasi Tambahan di playwright.config.ts

TYPESCRIPT
export default defineConfig({
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 2 : 4,
  reporter: [['html'], ['github']],
});
  • Retries di CI: Kalau test gagal, Playwright bakal coba ulang 2x sebelum menyerah. Ini safety net buat flaky test yang disebabkan environment CI.

  • Workers: Lebih sedikit worker di CI karena resource lebih terbatas.


Kesalahan Umum dan Cara Mengatasinya

Setelah bertahun-tahun ngurusin Playwright test suite (dan bikin hampir semua kesalahan yang mungkin dibuat), ini daftar error yang paling sering saya temuin — plus solusinya.

1. TimeoutError — Elemen Tidak Ditemukan

CODE
TimeoutError: locator.click: Timeout 30000ms exceeded.
Call log:
  - waiting for getByRole('button', { name: 'Submit' })
  - element was not found

Penyebab: Elemen memang belum muncul, atau locator lo salah.

Solusi:

  • Cek apakah elemen muncul setelah async operation. Kalau iya, tambahin explicit wait: await page.getByRole('button', { name: 'Submit' }).waitFor({ state: 'visible' })

  • Buka Inspector (--debug) dan verifikasi locator lo

  • Jangan pakai waitForTimeout() sebagai solusi — ini cuma nutupin masalah

2. Strict Mode Violation

CODE
Error: locator.click: Error: strict mode violation:
getByText('Lanjutkan') resolved to 2 elements

Penyebab: Ada dua elemen dengan teks "Lanjutkan" di halaman.

Solusi: Persempit scope dengan chaining atau gunakan .first() / .nth(0).

3. Element Not Visible

CODE
Error: locator.click: Element is not visible

Penyebab: Elemen ada di DOM tapi hidden — entah di luar viewport, atau kena CSS display: none.

Solusi: await element.scrollIntoViewIfNeeded() sebelum interaksi.

4. Test Lolos di Lokal, Gagal di CI

Ini bukan error spesifik — tapi skenario paling sering bikin frustrasi. Penyebab umum:

  • Viewport berbeda: CI runner biasanya pakai viewport lebih kecil. Set viewport eksplisit di config.

  • Resource terbatas: CI lebih lambat dari laptop lo. Naikkan timeout di config, atau tambahin explicit wait di step kritis.

  • Environment variable: Pastikan base URL, API key, dan credentials diset di CI environment.

5. Navigation Timeout

CODE
TimeoutError: page.waitForURL: Timeout 30000ms exceeded.
Expected URL: "https://aplikasi-lo.com/dashboard"
Received URL: "https://aplikasi-lo.com/login"

Penyebab: User stuck di halaman login — biasanya karena form submission gagal atau redirect nggak jalan.

Solusi: Buka Trace Viewer, cek network tab. Lihat apa yang server return setelah form disubmit.


Studi Kasus: Migrasi dari Selenium ke Playwright

Saya ingin cerita sedikit tentang pengalaman nyata — semoga bisa jadi gambaran buat Teman-Teman yang lagi mempertimbangkan migrasi.

Background

Tim saya waktu itu maintain test suite Selenium berisi sekitar 350 test end-to-end untuk aplikasi SaaS. Stack: Java + TestNG + Selenium Grid. Test jalan di Jenkins, durasi sekitar 2 jam 15 menit per run. Rata-rata 40-50 test gagal per run, dan setelah dicek ulang, sekitar 80% di antaranya adalah false positive — alias test yang sebenarnya bukan bug, tapi flaky.

Challenge

Masalah utama:

  • Flaky test karena timing issue — Selenium nggak punya auto-wait bawaan

  • Cross-browser setup ribet — tiap browser butuh WebDriver dan konfigurasi terpisah

  • Debugging failure di CI — cuma dapat stack trace dan screenshot, hampir mustahil reproduce

  • Durasi test — 2+ jam itu terlalu lama buat feedback loop yang sehat

Approach

Kami mutusin buat migrasi bertahap, bukan big-bang rewrite:

  1. Setup Playwright di samping Selenium (jalan paralel dulu, nggak langsung ganti)

  2. Migrasi test baru ke Playwright — semua fitur baru testing-nya pakai Playwright

  3. Rewrite test paling flaky — identifikasi 50 test paling sering gagal, rewrite di Playwright

  4. Bertahap rewrite sisanya — target 20-30 test per sprint, sambil tetap maintain Selenium suite

  5. Pensiunkan Selenium setelah semua test migrated

Implementation

Beberapa adaptasi yang kami lakukan:

  • Dari CSS selector ke semantic locator: Kami refactor strategy locator. Butuh effort ekstra di awal karena harus nambahin aria-label dan data-testid di komponen, tapi hasilnya worth it banget.

  • StorageState untuk auth: Dulu tiap test di Selenium login via UI (~3-4 detik per test). Dengan storageState, kami hilangkan overhead ini.

  • Network mocking: Test yang sebelumnya bergantung ke API pihak ketiga (payment gateway, map service) kami mock — jadi test jalan konsisten tanpa ketergantungan eksternal.

Results

Setelah migrasi penuh (sekitar 3 bulan, 2 sprint):

Metrik

Sebelum (Selenium)

Sesudah (Playwright)

Perubahan

Durasi full suite

135 menit

28 menit

-79%

Flaky test rate

12-15%

2-3%

-80%

Waktu debugging failure CI

30-45 menit

5-10 menit

-78%

Test yang reliable di 3 browser

Chrome only (Firefox/Safari opsional)

Chrome + Firefox + WebKit

3x coverage

Coverage mobile

Manual testing

Automated (device emulation)

Baru ada

Key Learnings

  1. Migrasi bertahap itu kunci. Jangan big-bang. Biarkan dua framework jalan barengan selama transisi.

  2. Invest di locator strategy upfront. Waktu yang lo habisin buat nambahin data-testid dan aria-label bakal balik modal berkali-kali lipat.

  3. Trace Viewer itu lifesaver. Kalau lo tim lead/QA lead, pastikan semua developer di tim tahu cara pakai Trace Viewer.

  4. StorageState menghemat lebih dari yang lo kira. 3 detik login × 350 test = hampir 18 menit. Dihilangkan sepenuhnya.


Tips dan Best Practices

Biar test suite Playwright lo tetap waras seiring pertumbuhan, ini beberapa prinsip yang saya pegang:

1. Prioritaskan Semantic Locator

CODE
❌ page.locator('#login-btn')           // fragile, bergantung DOM structure
❌ page.locator('button.btn-primary')  // mudah pecah saat refactor CSS
✅ page.getByRole('button', { name: 'Login' })  // stabil, berdasarkan aksesibilitas

2. Hindari Hard Wait

CODE
❌ await page.waitForTimeout(3000); // bikin test lambat & nggak reliable
✅ await page.getByText('Data tersimpan').waitFor({ state: 'visible' }); // kondisi-based

3. Gunakan Fixtures untuk Setup Berulang

Daripada copy-paste kode login di 50 file test, bikin custom fixture:

TYPESCRIPT
const test = base.extend({
  authenticatedPage: async ({ page }, use) => {
    await login(page);
    await use(page);
  },
});

4. Konfigurasi Trace dengan Bijak

TYPESCRIPT
use: {
  trace: 'on-first-retry', // rekam saat retry pertama — best balance
  screenshot: 'only-on-failure',
  video: 'retain-on-failure',
}

'on' untuk semua test bikin storage gede banget di CI. 'on-first-retry' adalah sweet spot.

5. Group Test dengan test.describe

TYPESCRIPT
test.describe('Fitur Login', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/login');
  });

  test('login dengan kredensial valid', async ({ page }) => {
    // ...
  });

  test('menampilkan error untuk password salah', async ({ page }) => {
    // ...
  });
});

Ini bikin test terstruktur, mudah dibaca, dan hooks-nya scoped — nggak ngeganggu test di luar group.

6. Jangan Abaikan Test Isolation

Kalau lo punya test yang sharing state (misal: test A create user, test B pakai user yang sama), itu bom waktu. Playwright kasih isolation gratis — manfaatkan. Kalau beneran butuh shared state, gunakan storageState atau fixtures, bukan dependency antar test.


Siapa yang Cocok Pakai Playwright — dan Siapa yang Sebaiknya Pertimbangkan Alternatif

Biar gamblang, nggak semua tim perlu buru-buru pindah ke Playwright. Ini panduan objektifnya.

Playwright Cocok Banget Buat Lo Kalau:

  • Lo develop aplikasi web modern (React, Angular, Vue, Svelte) dengan banyak interaksi dinamis

  • Lo butuh cross-browser testing serius — Chrome, Firefox, dan Safari dalam satu suite

  • Lo pengen parallel execution tanpa setup Grid yang ribet

  • Lo punya CI/CD pipeline dan butuh feedback cepat

  • Lo sering berurusan dengan network request mocking dan API interception

  • Tim lo udah nyaman dengan JavaScript atau TypeScript (atau Python, Java, .NET)

Pertimbangkan Alternatif Kalau:

  • Lo punya legacy system dengan investasi besar di Selenium dan migrasi bakal disruptif — dalam kasus ini, mungkin Playwright bisa diadopsi bertahap untuk fitur baru

  • Lo cuma testing internal tool sederhana yang cuma dipakai di satu browser — Cypress mungkin cukup dan lebih simpel

  • Stack tim lo bukan JavaScript dan belum ada rencana migrasi — meskipun Playwright support Python, Java, dan .NET, ekosistem JS/TS dapat update paling awal

  • Lo butuh real device testing (bukan emulasi) — Playwright emulasi mobile udah powerful, tapi untuk testing di device fisik lo tetap perlu layanan seperti BrowserStack atau Sauce Labs


Visual Regression Testing: Jaga Tampilan UI Tetap Konsisten

Pernah ngalamin situasi di mana lo update CSS satu komponen, eh tanpa sengaja tampilan halaman lain ikut berubah? Atau setelah refactor struktur DOM, layout jadi geser-geser sendiri? Ini masalah klasik yang susah ditangkap sama test fungsional biasa. Di sinilah visual regression testing main peran.

Playwright punya fitur screenshot comparison bawaan yang powerful banget. Konsepnya simpel: lo ambil screenshot halaman atau komponen, simpan sebagai baseline, lalu di setiap test run berikutnya Playwright bakal bandingin screenshot terbaru sama baseline. Kalau ada perbedaan pixel, test bakal gagal.

Cara Setup Visual Testing

Pertama, pastikan konfigurasi screenshot udah bener di playwright.config.ts:

TYPESCRIPT
use: {
  screenshot: 'on',
},

Lalu di test lo, tinggal pakai assertion toHaveScreenshot():

TYPESCRIPT
test('halaman dashboard tetap konsisten', async ({ page }) => {
  await page.goto('/dashboard');
  await expect(page).toHaveScreenshot('dashboard.png', {
    fullPage: true, // tangkap seluruh halaman, bukan cuma viewport
  });
});

Pertama kali test dijalankan, Playwright bakal bikin screenshot baseline di folder tests/__screenshots__/. Run berikutnya akan otomatis ngebandingin.

Mengatasi Perbedaan Kecil yang Wajar

Kadang ada perbedaan pixel yang wajar — misalnya render font yang sedikit beda antar OS, atau animasi loading yang posisinya nggak persis sama. Buat handle ini, lo bisa atur threshold:

TYPESCRIPT
await expect(page).toHaveScreenshot('dashboard.png', {
  maxDiffPixelRatio: 0.02, // toleransi 2% pixel berbeda
  threshold: 0.1,          // toleransi perbedaan warna per pixel
});

Screenshot Untuk Komponen Spesifik

Nggak harus full page. Lo bisa tangkap screenshot elemen tertentu aja:

TYPESCRIPT
test('tombol submit tampil dengan benar', async ({ page }) => {
  await page.goto('/form');
  const submitBtn = page.getByRole('button', { name: 'Kirim' });
  await expect(submitBtn).toHaveScreenshot('submit-button.png');
});

Ini berguna banget buat design system atau component library — lo bisa deteksi perubahan visual di level komponen sebelum naik ke halaman penuh.

Tips Praktis Visual Testing

Dari pengalaman, ada beberapa hal yang bikin visual testing lancar:

  • Konsistenin environment: Screenshot di macOS bisa beda sama Linux (soal font rendering). Kalau CI lo pakai Linux dan developer pakai macOS, hasil screenshot bakal mismatch. Solusinya: commit baseline dari CI, atau pakai Docker buat development testing.

  • Jangan screenshot konten dinamis: Tanggal, jam, data random — ini pasti gagal. Mock datanya atau exclude area yang dinamis pakai mask option.

  • Simpan baseline di Git: File screenshot baseline harus di-commit. Jangan masukin ke .gitignore.

  • Jangan keseringan update baseline: Kalau tiap kali test gagal lo langsung update baseline tanpa ngecek, visual testing lo jadi useless. Baseline berubah harus intentional — setelah lo verifikasi bahwa perubahan visual memang disengaja.


Component Testing: Test UI Tanpa Buka Browser

Salah satu fitur yang makin mateng di Playwright adalah Component Testing. Kalau sebelumnya Playwright identik sama E2E testing yang butuh browser beneran, sekarang lo bisa testing komponen UI secara terisolasi — mirip kayak Jest + Testing Library, tapi dengan engine yang sama kayak E2E test lo.

Kenapa Component Testing Penting?

E2E testing itu mahal. Dia lambat, butuh setup environment, dan kalau gagal, lo harus nge-track apakah gagalnya di frontend, backend, atau jaringan. Component testing duduk di tengah-tengah antara unit test (yang cepet tapi terbatas) dan E2E (yang komprehensif tapi lambat). Lo bisa verifikasi behavior komponen secara terisolasi, dengan kecepatan yang jauh lebih tinggi.

Setup Component Testing

Playwright support component testing untuk React, Vue, Svelte, dan Solid. Setup-nya:

BASH
npm init playwright@latest -- --ct

Ini bakal nambahin config terpisah: playwright-ct.config.ts. Struktur foldernya kurang lebih:

CODE
src/
  components/
    Button.tsx
    Button.spec.tsx   // file test component
playwright-ct.config.ts

Contoh: Testing Komponen React

TYPESCRIPT
// Button.spec.tsx
import { test, expect } from '@playwright/experimental-ct-react';
import Button from './Button';

test('tombol merender label dengan benar', async ({ mount }) => {
  const component = await mount(<Button label="Daftar Sekarang" />);
  await expect(component).toContainText('Daftar Sekarang');
});

test('memanggil onClick saat diklik', async ({ mount }) => {
  let clicked = false;
  const component = await mount(
    <Button label="Klik" onClick={() => { clicked = true; }} />
  );
  await component.click();
  expect(clicked).toBe(true);
});

test('tombol dalam state disabled tidak bisa diklik', async ({ mount }) => {
  let clicked = false;
  const component = await mount(
    <Button label="Submit" disabled onClick={() => { clicked = true; }} />
  );
  await component.click({ force: true });
  expect(clicked).toBe(false);
});

Yang keren, lo tetap bisa pakai semua Playwright API yang udah familiar: locator, assertion, bahkan network interception. Jadi nggak ada learning curve tambahan.

Component vs E2E: Kapan Pakai yang Mana?

Saya biasanya pakai aturan praktis gini:

  • Component test: Buat logic interaksi kompleks dalam satu komponen (form validation, dropdown behavior, modal open/close, state management UI).

  • E2E test: Buat flow yang melibatkan navigasi antar halaman, integrasi dengan API asli, atau skenario multi-user.

Dengan kombinasi keduanya, lo dapet testing trophy yang seimbang — coverage luas tanpa suite yang overweight.


Playwright dalam Docker: Lingkungan CI yang Konsisten

Masalah "di laptop gue jalan, di CI nggak" sering banget muncul gara-gara perbedaan environment. Solusi paling solid? Jalanin Playwright di dalam Docker container — baik untuk development lokal maupun CI.

Kenapa Docker?

  • Konsistensi OS: Mau developer pakai macOS, Windows, atau Linux, container-nya sama: Linux dengan semua dependency yang dibutuhkan.

  • Isolasi: Nggak perlu install browser binary secara manual di tiap mesin developer.

  • CI-ready: Image yang sama dipakai di lokal bisa langsung dipakai di GitHub Actions, GitLab CI, Jenkins, atau platform CI/CD apa pun.

Setup Docker untuk Playwright

Microsoft udah sediakan official Docker image. Bikin Dockerfile simpel kayak gini:

DOCKERFILE
FROM mcr.microsoft.com/playwright:v1.45.0-jammy

WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .

CMD ["npx", "playwright", "test"]

Jalankan:

BASH
docker build -t playwright-tests .
docker run playwright-tests

Docker Compose untuk Development

Biar makin enak, pakai docker-compose.yml:

YAML
version: '3.8'
services:
  playwright:
    image: mcr.microsoft.com/playwright:v1.45.0-jammy
    working_dir: /app
    volumes:
      - .:/app
      - /app/node_modules
    command: npx playwright test

Dengan volume mount, lo bisa edit kode di lokal dan langsung jalanin test di dalam container — tanpa install apa pun selain Docker.

Tips Performa di Docker

  • Gunakan base image yang tepat: jammy (Ubuntu 22.04) lebih ringan daripada focal. Untuk CI yang butuh startup cepat, pertimbangkan playwright:latest dengan tag spesifik.

  • Cache layer dengan bijak: Letakkan npm ci sebelum COPY . . di Dockerfile supaya dependency nggak di-install ulang tiap kali kode berubah.

  • Jangan lupa --shm-size: Chrome/Chromium butuh shared memory yang cukup. Di Docker, default /dev/shm cuma 64MB, yang bisa bikin browser crash. Tambahkan --shm-size=2g di docker run atau di compose config.


API Testing Langsung Pakai Playwright

Satu hal yang sering bikin orang kaget: Playwright bisa dipakai buat API testing — bahkan tanpa membuka browser sama sekali. Lo bisa ngirim HTTP request, verifikasi response, dan chain request pakai API yang sama kayak test browser lo.

Mengapa API Testing di Playwright?

Daripada install library terpisah kayak Axios atau Supertest, lo bisa manfaatin request fixture bawaan Playwright. Keuntungannya:

  • Satu tool buat E2E dan API testing — setup lebih simpel

  • Bisa sharing konfigurasi (baseURL, auth token, environment variables)

  • Reporting terintegrasi — hasil API test masuk ke HTML Report yang sama

Contoh: Test REST API

TYPESCRIPT
import { test, expect } from '@playwright/test';

test('GET /api/users mengembalikan daftar user', async ({ request }) => {
  const response = await request.get('https://jsonplaceholder.typicode.com/users');
  
  expect(response.status()).toBe(200);
  
  const users = await response.json();
  expect(users.length).toBeGreaterThan(0);
  expect(users[0]).toHaveProperty('name');
  expect(users[0]).toHaveProperty('email');
});

test('POST /api/users membuat user baru', async ({ request }) => {
  const response = await request.post('https://jsonplaceholder.typicode.com/users', {
    data: {
      name: 'Rizki Pratama',
      email: '[email protected]',
    },
  });
  
  expect(response.status()).toBe(201);
  
  const user = await response.json();
  expect(user.name).toBe('Rizki Pratama');
});

Autentikasi di API Test

Kalau API lo butuh token, setup di config aja:

TYPESCRIPT
// playwright.config.ts
use: {
  baseURL: 'https://api.aplikasi-lo.com',
  extraHTTPHeaders: {
    'Authorization': `Bearer ${process.env.API_TOKEN}`,
  },
},

Sekarang semua request otomatis nyertakan header auth — nggak perlu copy-paste di tiap test.

Hybrid Testing: API + E2E dalam Satu Flow

Ini pattern yang sering saya pakai untuk test yang efisien: setup data lewat API, verifikasi UI:

TYPESCRIPT
test('user baru muncul di daftar setelah registrasi via API', async ({ request, page }) => {
  // Setup: buat user via API (lebih cepat daripada lewat UI)
  await request.post('/api/users', {
    data: { name: 'Dewi Anggraini', email: '[email protected]' },
  });

  // Verifikasi: buka halaman daftar user dan cek UI
  await page.goto('/users');
  await expect(page.getByText('Dewi Anggraini')).toBeVisible();
});

Pola ini motong waktu test signifikan karena lo skip step-step UI yang repetitif buat setup data. Fokus E2E test lo tetap di verifikasi flow yang emang butuh interaksi browser.


Custom Fixtures: Bikin Test Lebih Modular

Semakin gede suite test lo, semakin banyak kode setup yang berulang. Playwright ngasih solusi lewat custom fixtures — lo bisa extend fixture bawaan dengan behavior spesifik buat proyek lo.

Contoh: Fixture untuk Halaman yang Butuh Login

TYPESCRIPT
// fixtures.ts
import { test as base, expect } from '@playwright/test';

export const test = base.extend({
  authenticatedPage: async ({ page }, use) => {
    await page.goto('/login');
    await page.getByLabel('Email').fill(process.env.TEST_EMAIL!);
    await page.getByLabel('Password').fill(process.env.TEST_PASSWORD!);
    await page.getByRole('button', { name: 'Masuk' }).click();
    await page.waitForURL('/dashboard');
    await use(page);
  },

  adminPage: async ({ browser }, use) => {
    const context = await browser.newContext({
      storageState: '.auth/admin.json',
    });
    const page = await context.newPage();
    await use(page);
    await context.close();
  },
});

Trus di test lo tinggal:

TYPESCRIPT
import { test, expect } from './fixtures';

test('dashboard menampilkan data user', async ({ authenticatedPage }) => {
  await authenticatedPage.goto('/dashboard');
  await expect(authenticatedPage.getByText('Selamat datang')).toBeVisible();
});

Bersih, reusable, dan nggak ada kode login yang copy-paste di 50 file test berbeda.

Override Fixture untuk Skenario Spesifik

Yang keren dari fixture system Playwright, lo bisa override fixture untuk test tertentu tanpa ngerusak yang lain:

TYPESCRIPT
test.describe('Sebagai user premium', () => {
  test.use({ storageState: '.auth/premium-user.json' });
  
  test('bisa akses fitur eksklusif', async ({ page }) => {
    await page.goto('/premium');
    await expect(page.getByText('Fitur Premium')).toBeVisible();
  });
});

Dengan pattern ini, suite test 500+ pun tetap manageable karena setup logic terpusat di satu tempat. Begitu flow login berubah, lo cuma update satu file fixture — bukan 500 test.


Test Tagging dan Selective Execution

Begitu suite test lo tumbuh, ngejalanin semua test tiap kali commit jadi nggak praktis. Playwright support test tagging buat ngegroup dan ngejalanin subset test berdasarkan kategori.

Cara Pakai Tag

TYPESCRIPT
test('login dengan Google OAuth @smoke @auth', async ({ page }) => {
  // ...
});

test('checkout dengan 3 item @regression @checkout @slow', async ({ page }) => {
  test.slow(); // kasih timeout 3x lebih lama
  // ...
});

Trus jalanin berdasarkan tag:

BASH
# Hanya test smoke
npx playwright test --grep @smoke

# Abaikan test lambat
npx playwright test --grep-invert @slow

# Kombinasi
npx playwright test --grep '@smoke|@critical'

Strategy Tagging yang Masuk Akal

Saya biasanya pakai sistem tag kayak gini:

  • @smoke — Test paling kritis yang harus selalu hijau. Jalan di tiap commit.

  • @regression — Test lengkap. Jalan sekali sebelum release atau nightly.

  • @slow — Test yang butuh waktu di atas 10 detik (upload file, proses data besar).

  • @flaky — Test yang udah diketahui flaky, lagi dalam proses perbaikan. Di-skip di CI strict mode.

Dengan tagging, lo bisa atur workflow CI yang bertingkat: smoke test di tiap PR, regression test nightly, dan full suite sebelum production release. Ini bikin feedback loop tetap cepat tanpa mengorbankan coverage. Kalau lo pakai GitHub Actions dengan workflow bertingkat, setup-nya simpel dan langsung jalan.


Playwright dengan TypeScript: Type Safety di Test Suite

Saya udah nyebutin TypeScript beberapa kali di artikel ini, tapi ada baiknya kita bahas lebih dalem: kenapa TypeScript itu game-changer untuk test suite yang gede, dan gimana cara maksimalinnya.

Kenapa TypeScript?

Test suite itu kode produksi juga. Kalau lo nulis TypeScript di aplikasi tapi JavaScript di test, lo kehilangan semua benefit type safety di layer yang justru paling krusial buat kualitas produk. Dengan TypeScript:

  • Autocomplete di editor: Lo nggak perlu inget-inget API Playwright. Tinggal ketik page. dan editor lo langsung nampilin suggestion.

  • Catch error sebelum runtime: Typo di nama fixture, parameter yang kurang, tipe data salah — semua ketahuan pas compile, bukan pas test jalan 5 menit.

  • Refactor lebih aman: Ganti nama fungsi atau fixture? TypeScript kasih tau semua tempat yang perlu diupdate.

Konfigurasi tsconfig untuk Playwright

JSON
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noEmit": true,
    "types": ["node"]
  },
  "include": ["tests/**/*.ts", "playwright.config.ts"]
}

Custom Matcher Type Safety

Kalau lo bikin custom matcher atau fixture, lo bisa extend type definition biar autocomplete tetap jalan:

TYPESCRIPT
// types/playwright.d.ts
import { Page } from '@playwright/test';

declare module '@playwright/test' {
  interface Page {
    loginAsAdmin(): Promise<void>;
    createDummyUser(data: { name: string }): Promise<void>;
  }
}

Dengan ini, page.loginAsAdmin() bakal muncul di autocomplete semua test file lo. Tim jadi lebih produktif karena nggak perlu bolak-balik buka file helper buat nyari nama fungsi. Microsoft Learn punya modul TypeScript untuk developer JavaScript yang bisa jadi titik awal kalau Teman-Teman baru transisi.

Kesimpulan

Setelah ngulik dari setup awal sampai optimasi CI, satu hal yang makin jelas: Playwright bukan sekadar alat testing biasa. Ia dibangun dari bawah dengan pemahaman bahwa web modern itu kompleks — penuh dengan async operations, shadow DOM, iframe lintas origin, dan animasi yang timing-nya nggak bisa ditebak. Semua itu di-handle secara native tanpa perlu plugin tambahan atau workaround yang bikin pusing.

Dari sisi engineering, kombinasi Playwright dengan TypeScript adalah duet yang susah ditandingin. Autocomplete yang presisi, type checking di compile time, dan kemampuan extend type definition bikin developer experience-nya naik level — terutama begitu test suite lo tumbuh jadi ratusan atau ribuan skenario. Strategy tagging dengan @smoke, @regression, @slow, dan @flaky juga bukan cuma soal organisasi kode, tapi soal gimana lo mendesain CI pipeline yang efisien: cepat saat dibutuhkan, menyeluruh saat waktunya tepat. Ini adalah pola pikir yang membedakan tim yang testing-nya sekadar "ada" dengan tim yang testing-nya jadi competitive advantage.

Pada akhirnya, testing tools yang baik adalah yang lo dan tim lo mau pakai setiap hari — bukan yang dipaksakan karena aturan. Playwright, dengan DX-nya yang matang dan ekosistem yang terus berkembang, berhasil berada di titik itu. Kalau lo serius soal kualitas software dan belum nyobain Playwright, saat ini adalah waktu yang tepat. Mulai dari satu test, taruh di CI, dan biarin hasilnya bicara sendiri. Kalau butuh panduan lebih lanjut, dokumentasi resmi Playwright adalah tempat terbaik untuk lanjut eksplorasi — ditulis dengan kualitas yang sama bagusnya dengan tool-nya sendiri.


Referensi

Playwright. (2026). Fast and reliable end-to-end testing for modern web apps.

Playwright. (2026). Installation.

GitHub. (2026). Playwright: A framework for web testing and automation.

Wikipedia. (2026). Playwright (software).

Microsoft Developer. (2026). The complete Playwright end-to-end story: Tools, AI, and real-world workflows.

Microsoft Learn. (2026). Build your first end-to-end test with Playwright.

AutomationTestingHub. (2026). Playwright automation testing tutorial.

BrowserStack. (2026). Playwright automation framework: Tutorial.

npm. (2026). Playwright.

Software Testing Tutorials & Automation. (2026). Playwright automation framework: 2026 tutorial guide.

Komentar (0)

Belum ada komentar. Jadilah yang pertama berbagi pendapat!

Tinggalkan komentar