Menguasai Playwright untuk web automation: navigation, interaction, assertions, fixtures, dan test suite yang terstruktur dan maintainable

Setelah di episode 4 kita mempelajari locators, pada episode ini kita mendalami Playwright — tool utama QA automation engineer. Playwright modern, cepat, dan punya fitur yang sangat membantu untuk E2E testing.
// Basic navigation
await page.goto('http://localhost:3000');
// Wait for specific load state
await page.goto('http://localhost:3000', { waitUntil: 'networkidle' });
// Reload
await page.reload();
// Navigate back/forward
await page.goBack();
await page.goForward();// Fill input
await page.fill('#email', 'user@test.com');
await page.fill('#password', 'password123');
// Alternative: getByLabel
await page.getByLabel('Email').fill('user@test.com');
// Select dropdown
await page.selectOption('#country', 'ID');
// Checkbox
await page.check('#terms');
await page.uncheck('#terms');
// Radio button
await page.check('#option-1');
// File upload
await page.setInputFiles('#file', 'path/to/file.pdf');
// Date picker
await page.fill('#date', '2026-01-15');// Click
await page.click('#submit-button');
await page.getByRole('button', { name: 'Submit' }).click();
// Double click
await page.dblclick('.item');
// Right click
await page.click('.item', { button: 'right' });
// Hover
await page.hover('.dropdown-trigger');
await page.locator('.dropdown-menu').toBeVisible();
// Drag and drop
await page.dragAndDrop('#source', '#target');// Type text
await page.keyboard.type('Hello World');
// Special keys
await page.keyboard.press('Enter');
await page.keyboard.press('Tab');
await page.keyboard.press('Escape');
// Key combinations
await page.keyboard.press('Control+a');
await page.keyboard.press('Control+c');// page, browser, context — built-in
test('my test', async ({ page, browser, context }) => {
// page: current page
// browser: browser instance
// context: browser context (isolated session)
});// fixtures.ts
import { test as base } from '@playwright/test';
type MyFixtures = {
loginPage: LoginPage;
dashboardPage: DashboardPage;
};
export const test = base.extend<MyFixtures>({
loginPage: async ({ page }, use) => {
const loginPage = new LoginPage(page);
await use(loginPage);
},
dashboardPage: async ({ page }, use) => {
const dashboardPage = new DashboardPage(page);
await use(dashboardPage);
},
});
export { expect } from '@playwright/test';// Global setup
test.beforeAll(async () => {
// Setup database, start server, etc.
});
// Per-test setup
test.beforeEach(async ({ page }) => {
await page.goto('http://localhost:3000');
});
// Per-test cleanup
test.afterEach(async ({ page }) => {
// Clear cookies, localStorage
await page.context().clearCookies();
});
// Global cleanup
test.afterAll(async () => {
// Cleanup database, stop server, etc.
});// Page-level
await expect(page).toHaveURL('/dashboard');
await expect(page).toHaveTitle('Dashboard');
// Element-level
await expect(page.locator('h1')).toHaveText('Welcome');
await expect(page.locator('.alert')).toBeVisible();
await expect(page.locator('.counter')).toHaveCount(5);
// Custom matchers
await expect(page.locator('.price')).toHaveText(/\$\d+\.\d{2}/);Note
Playwright punya auto-wait built-in — tidak perlu page.waitForTimeout() atau manual sleeps. Gunakan assertions sebagai waiting mechanism.
import { test, expect } from '@playwright/test';
test.describe('Shopping Cart', () => {
test.beforeEach(async ({ page }) => {
await page.goto('http://localhost:3000/products');
});
test('add item to cart', async ({ page }) => {
// Find and click "Add to Cart"
await page.getByRole('button', { name: 'Add to Cart' }).first().click();
// Go to cart
await page.getByRole('link', { name: 'Cart' }).click();
// Assert item in cart
await expect(page.locator('.cart-item')).toHaveCount(1);
await expect(page.locator('.cart-total')).toContainText('$');
});
test('remove item from cart', async ({ page }) => {
// Add item first
await page.getByRole('button', { name: 'Add to Cart' }).first().click();
await page.getByRole('link', { name: 'Cart' }).click();
// Remove item
await page.getByRole('button', { name: 'Remove' }).click();
// Assert cart empty
await expect(page.locator('.cart-empty')).toBeVisible();
});
});page.goto(), waitUntil, reload, back/forward.Di episode 6 selanjutnya kita akan membahas Cypress & alternatives — kapan pakai Cypress vs Playwright, dan perbandingan fitur. Sampai jumpa di episode 6!