Playwright Basics
Learn the fundamentals of Playwright selectors and actions used in browser journeys.
Playwright Basics
tracer uses Playwright under the hood for browser automation. Understanding Playwright basics will help you create more reliable journeys.
Selectors
Selectors identify elements on the page. Playwright supports multiple selector strategies.
CSS Selectors
Standard CSS selectors work directly:
// By ID
page.click('#submit-button');
// By class
page.click('.btn-primary');
// By attribute
page.click('[data-testid="submit"]');
// By tag and attribute
page.click('button[type="submit"]');
// Nested elements
page.click('.form .submit-btn');Text Selectors
Find elements by their text content:
// Exact match
page.click('text=Submit');
// Case-insensitive match
page.click('text=submit');
// Partial match
page.click('text=Sign');
// Regex match
page.click('text=/sign (in|up)/i');Role Selectors
Find by ARIA role (most reliable for accessible apps):
// Button with specific text
page.click('role=button[name="Submit"]');
// Link
page.click('role=link[name="Learn more"]');
// Heading
page.locator('role=heading[name="Dashboard"]');Test ID Selectors
The recommended approach for stable tests:
// In your HTML
<button data-testid="submit-btn">Submit</button>
// In your journey
page.click('[data-testid="submit-btn"]');Best Practice: Add data-testid attributes to key elements. They're more stable than CSS classes which may change for styling reasons.
Combining Selectors
Chain selectors for precision:
// Button containing text "Submit" inside form
page.click('.login-form >> button:has-text("Submit")');
// First item in a list
page.click('.item-list >> li >> nth=0');Common Actions
Navigation
// Go to URL
await page.goto('https://example.com');
// Wait for navigation
await page.waitForURL('**/dashboard');
// Go back/forward
await page.goBack();
await page.goForward();
// Reload
await page.reload();Clicking
// Simple click
await page.click('#button');
// Double click
await page.dblclick('#element');
// Right click
await page.click('#element', { button: 'right' });
// Click at position
await page.click('#element', { position: { x: 10, y: 10 } });
// Force click (skip visibility checks)
await page.click('#element', { force: true });Typing
// Fill input (clears first)
await page.fill('#email', '[email protected]');
// Type character by character
await page.type('#search', 'query', { delay: 100 });
// Clear input
await page.fill('#input', '');
// Press keys
await page.press('#input', 'Enter');
await page.press('#input', 'Control+a');Selecting Options
// Select by value
await page.selectOption('#country', 'US');
// Select by label
await page.selectOption('#country', { label: 'United States' });
// Select multiple
await page.selectOption('#colors', ['red', 'blue']);Checkboxes and Radio Buttons
// Check
await page.check('#agree');
// Uncheck
await page.uncheck('#newsletter');
// Set checked state
await page.setChecked('#option', true);File Upload
// Upload file
await page.setInputFiles('#upload', '/path/to/file.pdf');
// Upload multiple files
await page.setInputFiles('#upload', ['/file1.pdf', '/file2.pdf']);Waiting
Playwright auto-waits for elements, but sometimes you need explicit waits.
Wait for Element
// Wait for element to appear
await page.waitForSelector('.modal');
// Wait for element to be hidden
await page.waitForSelector('.loading', { state: 'hidden' });
// Wait for element to be attached to DOM
await page.waitForSelector('.dynamic', { state: 'attached' });Wait for Navigation
// Wait for page load
await page.waitForLoadState('load');
// Wait for network idle
await page.waitForLoadState('networkidle');
// Wait for DOM ready
await page.waitForLoadState('domcontentloaded');Wait for Network
// Wait for specific request
await page.waitForRequest('**/api/users');
// Wait for response
await page.waitForResponse('**/api/users');
// Wait with predicate
await page.waitForResponse(response =>
response.url().includes('/api/') && response.status() === 200
);Wait for Function
// Wait for custom condition
await page.waitForFunction(() => {
return document.querySelector('.data-loaded') !== null;
});Assertions
Use expect to verify page state.
Element Assertions
// Element is visible
await expect(page.locator('.header')).toBeVisible();
// Element is hidden
await expect(page.locator('.modal')).toBeHidden();
// Element has text
await expect(page.locator('.title')).toHaveText('Welcome');
// Element contains text
await expect(page.locator('.message')).toContainText('success');
// Element has value
await expect(page.locator('#email')).toHaveValue('[email protected]');
// Element has attribute
await expect(page.locator('button')).toHaveAttribute('disabled', '');
// Element count
await expect(page.locator('.item')).toHaveCount(5);Page Assertions
// URL check
await expect(page).toHaveURL('https://example.com/dashboard');
await expect(page).toHaveURL(/dashboard/);
// Title check
await expect(page).toHaveTitle('Dashboard - Example');
await expect(page).toHaveTitle(/Dashboard/);Handling Frames
// Get frame by name
const frame = page.frame('iframe-name');
// Get frame by URL
const frame = page.frame({ url: /embedded/ });
// Interact with frame
await frame.click('#button');Handling Popups and Dialogs
// Handle alert/confirm/prompt
page.on('dialog', async dialog => {
console.log(dialog.message());
await dialog.accept(); // or dialog.dismiss()
});
// Handle new tab/popup
const [popup] = await Promise.all([
page.waitForEvent('popup'),
page.click('a[target="_blank"]'),
]);
await popup.waitForLoadState();Screenshots
// Full page screenshot
await page.screenshot({ path: 'screenshot.png', fullPage: true });
// Element screenshot
await page.locator('.chart').screenshot({ path: 'chart.png' });
// With options
await page.screenshot({
path: 'screenshot.png',
type: 'jpeg',
quality: 80,
});Common Patterns
Login Flow
await page.goto('https://app.example.com/login');
await page.fill('#email', process.env.USER_EMAIL);
await page.fill('#password', process.env.USER_PASSWORD);
await page.click('button[type="submit"]');
await expect(page).toHaveURL(/dashboard/);Form Submission
await page.fill('#name', 'John Doe');
await page.fill('#email', '[email protected]');
await page.selectOption('#country', 'US');
await page.check('#agree');
await page.click('button:has-text("Submit")');
await expect(page.locator('.success')).toBeVisible();Search and Filter
await page.fill('.search-input', 'query');
await page.press('.search-input', 'Enter');
await page.waitForSelector('.results');
await expect(page.locator('.result-item')).toHaveCount.greaterThan(0);Debugging Tips
-
Use Playwright Inspector
PWDEBUG=1 npx playwright test -
Add screenshots at key points in your journey
-
Download trace files from failed runs and view in Trace Viewer
-
Check console logs for JavaScript errors
-
Verify selectors using browser DevTools
Playwright's official documentation has comprehensive guides and API references.