Playwright Academy
0 dari 17
Fase 2 · Jurus IntiModul 4 dari 17

Locator

Cara menemukan elemen: getByRole, getByLabel, getByText, test id, CSS, filter, nth, dan strict mode.

Locator yang baik menunjuk elemen berdasarkan apa elemen itu di mata pengguna, bukan urutan atau posisinya di kode halaman.

Di dunia QA

Saat menulis langkah test case, kamu menulis "klik tombol Masuk", bukan "klik kotak ketiga dari kiri". Locator yang baik ditulis dengan cara yang sama: menyebut elemen seperti pengguna melihatnya.

Apa itu locator?

Locator adalah petunjuk untuk menemukan elemen di halaman, misalnya tombol, kolom isian, atau judul. Tiga sifatnya:

  1. Tidak langsung mencari. Elemen baru dicari saat kamu beraksi (click, fill) atau mengecek sesuatu.
  2. Selalu dicari ulang. Kalau halaman berubah, locator menemukan elemen yang terbaru.
  3. Tidak mau menebak. Kalau cocok dengan lebih dari satu elemen saat mau beraksi, Playwright berhenti dan memberi tahu.
tombol = page.get_by_role("button", name="Masuk")   # belum mencari apa-apa
tombol.click()                                       # baru dicari di sini (dan ditunggu sampai siap)
const tombol = page.getByRole('button', { name: 'Masuk' }); // belum mencari apa-apa (tanpa await!)
await tombol.click();                                        // baru dicari di sini (dan ditunggu sampai siap)

Urutan locator yang dianjurkan

Utamakan apa yang dilihat pengguna. Makin atas, makin tahan terhadap perubahan tampilan:

#LocatorKapan dipakaiContoh di TokoQA
1get_by_role / getByRolePilihan pertama. Tombol, link, judul, checkbox, dan sejenisnyatombol "Masuk"
2get_by_label / getByLabelKolom isian yang punya labelkolom "Username"
3get_by_placeholder / getByPlaceholderKolom tanpa label, tapi ada teks contoh di dalamnya"Misalnya: mouse"
4get_by_text / getByTextTeks biasa yang tidak bisa diklik (paragraf, keterangan)"Stok habis"
5get_by_alt_text, get_by_titleGambar (dari teks alt-nya) atau elemen yang punya titlefoto di halaman detail produk ("Foto Mouse Wireless")
6get_by_test_id / getByTestIdKalau cara di atas tidak bisa dipakaidata-testid="cart-count"
7locator("css") / XPathPilihan terakhir#students tr

get_by_role mencari elemen seperti aplikasi pembaca layar untuk tunanetra. Kalau locator role sulit dibuat, biasanya aplikasinya kurang ramah aksesibilitas, dan itu temuan yang layak dilaporkan.

1. getByRole: locator andalan

Rumusnya: role (jenis elemennya) + name (tulisan yang terbaca oleh pengguna).

page.get_by_role("button", name="Masuk")  # tombol bertuliskan Masuk
page.get_by_role("heading", name="Masuk ke TokoQA")  # judul (h1 sampai h6)
page.get_by_role("link", name="Buka pusat bantuan")  # tautan
page.get_by_role("checkbox", name="Berlangganan newsletter")  # checkbox dengan label itu
page.get_by_role("textbox", name="Username")  # kolom teks berlabel Username
page.get_by_role("button", name="Masuk", exact=True)   # harus sama persis (huruf besar/kecil juga)
page.getByRole('button', { name: 'Masuk' }); // tombol bertuliskan Masuk
page.getByRole('heading', { name: 'Masuk ke TokoQA' }); // judul (h1 sampai h6)
page.getByRole('link', { name: 'Buka pusat bantuan' }); // tautan
page.getByRole('checkbox', { name: 'Berlangganan newsletter' }); // checkbox dengan label itu
page.getByRole('textbox', { name: 'Username' }); // kolom teks berlabel Username
page.getByRole('button', { name: 'Masuk', exact: true }); // harus sama persis (huruf besar/kecil juga)

Role yang paling sering kamu pakai:

RoleBiasanya elemen HTML ini
button<button>, <input type="submit">
link<a href="...">
heading<h1> sampai <h6> (judul)
textbox<input type="text/email/tel/url">, <textarea>. Kolom password tidak punya role, jadi cari dengan get_by_label
checkbox / radio<input type="checkbox/radio">
combobox<select> (dropdown)
row / cell<tr> / <td> (baris dan sel tabel)
listitem<li> (butir daftar)
alert / status / dialogelemen dengan role="alert" dan sejenisnya
Hati-hati dengan name

Secara bawaan, name dicocokkan sebagian dan tidak membedakan huruf besar/kecil. Jadi name="Masuk" juga cocok dengan tombol "Masuk Sekarang". Kalau harus persis, tambahkan exact=True / exact: true.

Locator lainnya (urutan 2 sampai 6)

page.get_by_label("Username")                 # input yang labelnya "Username"
page.get_by_placeholder("Misalnya: mouse")    # input dengan placeholder itu
page.get_by_text("Klik kanan di sini")        # elemen berisi teks itu
page.get_by_text("Apel", exact=True)          # teks persis "Apel"
page.get_by_alt_text("Foto Mouse Wireless")   # gambar dengan alt itu
page.get_by_test_id("cart-count")             # elemen dengan data-testid="cart-count"
page.getByLabel('Username');                  // input yang labelnya "Username"
page.getByPlaceholder('Misalnya: mouse');     // input dengan placeholder itu
page.getByText('Klik kanan di sini');         // elemen berisi teks itu
page.getByText('Apel', { exact: true });      // teks persis "Apel"
page.getByAltText('Foto Mouse Wireless');     // gambar dengan alt itu
page.getByTestId('cart-count');               // elemen dengan data-testid="cart-count"

data-testid adalah label khusus test yang ditambahkan developer. Stabil, tapi tidak terlihat pengguna, jadi test bisa lulus walaupun tombolnya tersembunyi. Karena itu test id ada di urutan bawah.

Coba sendiri: Locator Lab

Ketik locator di kotak sebelah kanan, gaya Python atau TypeScript, lalu lihat elemen mana yang diberi bingkai. Selesaikan tantangannya untuk mengumpulkan bintang.

Mempersempit pencarian

Halaman sungguhan punya banyak elemen mirip, misalnya 8 tombol "Tambah ke keranjang". Persempit pencarian: "tombol tambah di kartu Mouse Wireless".

# Chaining: cari di DALAM elemen lain
kartu_mouse = page.get_by_test_id("product-card").filter(has_text="Mouse Wireless")
kartu_mouse.get_by_role("button", name="Tambah ke keranjang").click()  # tombol tambah hanya di kartu Mouse

# filter(has=...): elemen yang MEMILIKI elemen tertentu di dalamnya
kartu = page.get_by_test_id("product-card").filter(
    has=page.get_by_role("heading", name="Mouse Wireless")
)

# filter(has_not_text=...): kebalikannya
tersedia = page.get_by_test_id("product-card").filter(has_not_text="Stok habis")

# Baris tabel yang mengandung "Citra" → tombol Detail di baris itu
page.get_by_role("row").filter(has_text="Citra").get_by_role("button", name="Detail").click()
// Chaining: cari di DALAM elemen lain
const kartuMouse = page.getByTestId('product-card').filter({ hasText: 'Mouse Wireless' });
await kartuMouse.getByRole('button', { name: 'Tambah ke keranjang' }).click(); // tombol tambah hanya di kartu Mouse

// filter({ has }): elemen yang MEMILIKI elemen tertentu di dalamnya
const kartu = page.getByTestId('product-card').filter({
  has: page.getByRole('heading', { name: 'Mouse Wireless' }),
});

// filter({ hasNotText }): kebalikannya
const tersedia = page.getByTestId('product-card').filter({ hasNotText: 'Stok habis' });

// Baris tabel yang mengandung "Citra" → tombol Detail di baris itu
await page.getByRole('row').filter({ hasText: 'Citra' }).getByRole('button', { name: 'Detail' }).click();

first, last, nth: pakai seperlunya

buah = page.get_by_test_id("fruit")  # locator yang cocok dengan 3 elemen
buah.first        # elemen pertama
buah.last         # elemen terakhir
buah.nth(1)       # elemen ke-2 (index mulai dari 0!)
buah.count()      # jumlah elemen yang cocok
const buah = page.getByTestId('fruit'); // locator yang cocok dengan 3 elemen
buah.first();     // elemen pertama
buah.last();      // elemen terakhir
buah.nth(1);      // elemen ke-2 (index mulai dari 0!)
await buah.count(); // jumlah elemen yang cocok
Memilih berdasarkan urutan itu rawan

nth(2) artinya "yang ketiga" (hitungannya mulai dari 0). Kalau urutannya berubah, misalnya ada produk baru, test bisa mengklik barang yang salah tanpa ada error sama sekali. Lebih aman memfilter berdasarkan teks. Pakai nth hanya kalau urutannya memang yang mau diuji, misalnya "produk teratas setelah diurutkan".

Strict mode: Playwright tidak mau menebak

Di Arena ada tiga tombol yang diawali kata "Tampilkan". Kalau kamu menulis:

page.get_by_role("button", name="Tampilkan").click()  # cocok dengan 3 tombol, jadi gagal
await page.getByRole('button', { name: 'Tampilkan' }).click(); // cocok dengan 3 tombol, jadi gagal

Playwright akan berhenti dan menampilkan pesan seperti ini:

Error: strict mode violation: get_by_role("button", name="Tampilkan") resolved to 3 elements:
    1) <button id="btn-alert">Tampilkan alert</button> aka get_by_role("button", name="Tampilkan alert")
    2) <button id="btn-confirm">Tampilkan confirm</button> aka get_by_role("button", name="Tampilkan confirm")
    3) <button id="btn-prompt">Tampilkan prompt</button> aka get_by_role("button", name="Tampilkan prompt")

Bagian aka berisi saran locator yang lebih tepat. Perjelas name, tambahkan filter, atau tulis .first dengan sengaja kalau memang mau yang pertama.

Contoh di PerpusQA (meja pustakawan, tab Katalog): get_by_label("Eksemplar Kosmos") cocok dengan 2 elemen, yaitu kolom jumlah dan tombol berlabel "Simpan eksemplar Kosmos". Pakai exact=True, atau sebut perannya.

page.get_by_label("Eksemplar Kosmos", exact=True)           # hanya kolom jumlah
page.get_by_role("spinbutton", name="Eksemplar Kosmos")     # alternatif: sebut perannya
page.getByLabel('Eksemplar Kosmos', { exact: true });         // hanya kolom jumlah
page.getByRole('spinbutton', { name: 'Eksemplar Kosmos' });   // alternatif: sebut perannya

CSS dan XPath: pilihan terakhir

page.locator("#students tr")              # CSS
page.locator("button.danger")             # CSS dengan class
page.locator("xpath=//table//tr[2]")      # XPath (hindari kalau bisa)
page.locator("article", has_text="Mouse") # CSS + filter teks
page.locator('#students tr');              // CSS
page.locator('button.danger');             // CSS dengan class
page.locator('xpath=//table//tr[2]');      // XPath (hindari kalau bisa)
page.locator('article', { hasText: 'Mouse' }); // CSS + filter teks
Hindari locator seperti ini

div > div:nth-child(3) > span.sc-AxjAm atau //*[@id="root"]/div[2]/div/button. Begitu developer mengubah tata letak, semua test-mu gagal padahal fiturnya baik-baik saja.

Semua contoh dalam file asli

tests/academy/test_m04_locators.pytests/academy/m04-locators.spec.tsLulus test
Memuat tests/academy/test_m04_locators.py…
uv run pytest tests/academy/test_m04_locators.py
Memuat tests/academy/m04-locators.spec.ts…
npx playwright test m04-locators --project=chromium

Cara cepat mendapatkan locator: jalankan codegen (Modul 8), klik Pick locator, lalu arahkan kursor ke elemen. Ekstensi Playwright di VS Code punya fitur yang sama.

Lebih jauh (opsional)Trik komunitas
  • describe() (v1.53+) memberi nama yang mudah dibaca pada locator. Kalau test gagal, laporannya langsung menyebut "badge jumlah keranjang", bukan kode yang panjang.
  • Hanya elemen yang terlihat. Python: locator.visible (tanpa kurung). TypeScript: locator.visible(). Berguna kalau ada elemen kembar yang tersembunyi, misalnya menu versi HP dan versi desktop.
  • Pengingat otomatis (TypeScript): plugin eslint-plugin-playwright punya aturan playwright/no-raw-locators yang menegur kalau kamu memakai CSS atau XPath mentah.
  • Tim memakai nama atribut lain? Kalau timmu memakai data-qa, cukup ubah sekali di konfigurasi, lalu get_by_test_id akan membaca atribut itu.
  • Pick locator di Inspector, UI Mode, atau ekstensi VS Code tetap cara tercepat untuk mendapatkan locator terbaik.
badge = page.get_by_test_id("cart-count").describe("badge jumlah keranjang")  # nama yang tampil di laporan kalau gagal
tombol = page.get_by_role("button", name="Masuk").visible      # properti, tanpa kurung

# conftest.py: kalau tim memakai data-qa
# playwright.selectors.set_test_id_attribute("data-qa")
const badge = page.getByTestId('cart-count').describe('badge jumlah keranjang'); // nama yang tampil di laporan kalau gagal
const tombol = page.getByRole('button', { name: 'Masuk' }).visible(); // hanya tombol yang terlihat

// playwright.config.ts: kalau tim memakai data-qa
// use: { testIdAttribute: 'data-qa' }

Latihan

Tulis locator-mu sendiri dulu, jalankan, baru buka jawabannya. Urutannya: role, label, teks, test id, baru CSS.

Latihan 1

Kumpulkan semua bintang di Locator Lab

Kerjakan semua tantangan di Locator Lab. Coba locator role atau label dulu sebelum CSS.

Hasilsemua tantangan berbintang

Latihan 2

Locator untuk halaman toko

Setelah login sebagai customer, tulis locator untuk:

  1. Kolom Cari produk.
  2. Dropdown Kategori.
  3. Angka di ikon keranjang.
  4. Tombol tambah di kartu Headset Gaming.

Lalu buktikan semuanya bekerja: tambah Headset Gaming ke keranjang, pilih kategori Furnitur, lalu cari meja.

Hasilangka keranjang 1, Furnitur berisi 2 produk, dan pencarian meja di kategori Furnitur menyisakan 1 produk (Meja Lipat)

Latihan 3

Pancing strict mode violation, lalu perbaiki

Di Arena, klik get_by_role("button", name="Detail") dan baca errornya. Lalu perbaiki supaya hanya mengklik Detail di baris Dewi.

Hasilpercobaan pertama gagal dengan strict mode violation (5 tombol), perbaikannya menampilkan Detail: Dewi

Latihan 4

Cari baris tabel berdasarkan isinya

Di tabel Arena (jurus 6), hitung baris berstatus Remedial, lalu klik Detail di baris Budi. Cari baris dari isinya, bukan posisinya.

Hasil2 baris Remedial, dan Detail: Budi