Generate end-to-end tests using Playwright with best practices for selectors, assertions, and page interactions
Generate robust end-to-end tests using Playwright with proper selectors, auto-waiting assertions, and page interactions following official best practices.
When asked to write Playwright tests, follow these steps:
1. **Understand the test requirements**
- Identify what functionality needs to be tested
- Determine which pages and user flows to cover
- Ask clarifying questions about expected behaviors if needed
2. **Structure the test file properly**
- Import test and expect from '@playwright/test'
- Use test.describe() to group related tests
- Add test.beforeEach() hooks for common setup (e.g., navigation)
- Use test.afterEach() for cleanup if needed
3. **Write navigation and page interactions**
- Start tests with `await page.goto('url')` for navigation
- Use Playwright's built-in locators (NEVER use CSS/XPath unless absolutely necessary):
- `page.getByRole('button', { name: 'Submit' })` for accessible elements
- `page.getByText('Welcome')` for text content
- `page.getByLabel('Email')` for form fields
- `page.getByPlaceholder('Enter email')` for inputs
- `page.getByTestId('submit-btn')` for test IDs
- Chain actions directly: `await page.getByRole('link', { name: 'Login' }).click()`
4. **Use proper actions**
- `.click()` for clicking elements
- `.fill('text')` for filling form fields
- `.check()` / `.uncheck()` for checkboxes
- `.selectOption('value')` for dropdowns
- `.hover()` for hover interactions
- `.press('Enter')` for keyboard actions
- `.setInputFiles('path')` for file uploads
5. **Write assertions that auto-wait**
- Use `await expect(locator).toBeVisible()` instead of manual waits
- Use `await expect(locator).toHaveText('expected')` for text checks
- Use `await expect(page).toHaveTitle(/regex/)` for page titles
- Use `await expect(page).toHaveURL('url')` for URL checks
- Use `await expect(locator).toBeEnabled()` / `.toBeDisabled()` for element states
- Use `await expect(locator).toHaveCount(3)` for counting elements
- NEVER add manual `page.waitForTimeout()` or `sleep()` calls
6. **Leverage test isolation**
- Each test receives a fresh page fixture with isolated browser context
- No need to manually clean up between tests
- Tests can run in parallel safely
7. **Follow best practices**
- Use descriptive test names that explain what is being tested
- Keep tests focused on a single user flow or feature
- Add comments only for complex logic, not obvious actions
- Use TypeScript for type safety (add `// @ts-check` for JavaScript)
- Avoid hardcoded waits - rely on auto-waiting
- Group related tests with test.describe()
```typescript
import { test, expect } from '@playwright/test';
test.describe('User Authentication', () => {
test.beforeEach(async ({ page }) => {
await page.goto('https://example.com/login');
});
test('should login with valid credentials', async ({ page }) => {
await page.getByLabel('Email').fill('[email protected]');
await page.getByLabel('Password').fill('password123');
await page.getByRole('button', { name: 'Login' }).click();
await expect(page).toHaveURL(/.*dashboard/);
await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible();
});
test('should show error with invalid credentials', async ({ page }) => {
await page.getByLabel('Email').fill('[email protected]');
await page.getByLabel('Password').fill('wrongpass');
await page.getByRole('button', { name: 'Login' }).click();
await expect(page.getByText('Invalid credentials')).toBeVisible();
await expect(page).toHaveURL(/.*login/);
});
});
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/playwright-e2e-test-writer/raw