Menguasai locators di Playwright: CSS selectors, XPath, accessibility-based selectors, dan best practices untuk locator yang robust dan maintainable

Setelah di episode 3 kita mempelajari test fundamentals, pada episode ini kita mendalami komponen paling kritis dalam test automation: locators. Locator yang buruk membuat test flaky dan sulit dipelihara.
// By ID (paling robust)
await page.click('#login-button');
// By class
await page.click('.btn-primary');
// By attribute
await page.click('button[type="submit"]');
// By text content
await page.click('button:has-text("Login")');
// By nth element
await page.click('.item:nth-child(2)');// By text
await page.click('//button[contains(text(), "Login")]');
// By attribute
await page.click('//input[@name="email"]');
// By index
await page.click('(//button)[1]');
// By parent
await page.click('//form//button[@type="submit"]');// By role (PREFERRED)
await page.getByRole('button', { name: 'Login' }).click();
await page.getByRole('link', { name: 'Dashboard' }).click();
await page.getByRole('textbox', { name: 'Email' }).fill('test@test.com');
// By label
await page.getByLabel('Email').fill('test@test.com');
await page.getByLabel('Password').fill('password123');
// By placeholder
await page.getByPlaceholder('Enter your email').fill('test@test.com');
// By text
await page.getByText('Welcome back').click();
// By test ID
await page.getByTestId('login-button').click();Priority (terbaik → terburuk):
1. getByRole() - most robust, accessibility-friendly
2. getByLabel() - good for forms
3. getByTestId() - stable if test-id attributes exist
4. CSS selectors - acceptable for simple cases
5. XPath - avoid if possible (fragile, hard to read)// BAD: Fragile selectors
await page.click('div > div > div > button'); // Structure-dependent
await page.click('.css-1a2b3c'); // Generated class names
await page.click('button:nth-child(47)'); // Position-dependent
// GOOD: Robust selectors
await page.getByRole('button', { name: 'Submit' }); // Semantic
await page.getByTestId('submit-button'); // Test-specific attribute
await page.locator('button').filter({ hasText: 'Submit' }); // Text-based// Playwright auto-waits (built-in)
await page.click('#button'); // Auto-waits for visible, enabled
// Explicit wait (if needed)
await page.locator('.loading').waitFor({ state: 'hidden' });
await page.locator('.result').waitFor({ state: 'visible' });<!-- Di HTML, tambahkan test-id attributes -->
<button data-testid="login-button">Login</button>
<input data-testid="email-input" />
<div data-testid="user-profile">...</div>// Gunakan test-id
await page.getByTestId('login-button').click();
await page.getByTestId('email-input').fill('test@test.com');Tip
Gunakan getByRole() sebagai default — ini adalah locator paling robust karena berbasis accessibility tree, bukan DOM structure.
// Contoh test dengan robust locators
test('login form', async ({ page }) => {
await page.goto('/login');
// Gunakan getByRole dan getByLabel
await page.getByRole('textbox', { name: 'Email' }).fill('user@test.com');
await page.getByRole('textbox', { name: 'Password' }).fill('password');
await page.getByRole('button', { name: 'Login' }).click();
// Assert dengan getByRole
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});getByRole(), getByLabel(), getByTestId() — most robust.Di episode 5 selanjutnya kita akan membahas web automation dengan Playwright — navigate, interact, assertions, dan fixtures untuk test suite yang terstruktur. Sampai jumpa di episode 5!