End-to-End Testing
End-to-End Testing
End-to-end (E2E) testing ensures that the entire WordPress stack—from the database to the browser—functions correctly. We use Playwright to simulate real user interactions, validate UI components, and verify critical workflows such as authentication, content publishing, and plugin integrations.
Prerequisites
Before running the E2E suite, ensure your environment meets the following requirements:
- Node.js: v18.x or higher.
- Local WordPress Instance: A running instance of the site (e.g., via LocalWP, Docker, or WP-Env).
- Test Data: It is recommended to run tests against a fresh installation or a dedicated staging database to avoid data loss.
Installation
Install the necessary dependencies and browser binaries using npm:
# Install dependencies
npm install
# Install Playwright browsers
npx playwright install --with-deps
Configuration
The E2E suite is configured via playwright.config.ts. You can override default behaviors using environment variables. Create a .env.testing file in your root directory to define your local environment details:
| Variable | Description | Default |
| :--- | :--- | :--- |
| BASE_URL | The URL of your WordPress site. | http://localhost:8888 |
| WP_ADMIN_USER | Admin username for backend tests. | admin |
| WP_ADMIN_PASSWORD | Admin password for backend tests. | password |
Running Tests
You can execute tests in various modes depending on your development workflow:
# Run all tests in headless mode
npm run test:e2e
# Run tests in a specific browser (chromium, firefox, or webkit)
npm run test:e2e -- --project=chromium
# Open Playwright UI for interactive debugging
npm run test:e2e -- --ui
# Run a specific test file
npx playwright test tests/specs/login.spec.ts
Validating User Flows
Tests are located in the tests/specs/ directory. Below is an example of a common user flow validation:
Example: Verifying the Homepage and Navigation
This test ensures the UI components render correctly and the primary navigation is functional.
import { test, expect } from '@playwright/test';
test.describe('Public Interface Flow', () => {
test('should load the homepage and check for branding', async ({ page }) => {
await page.goto('/');
// Validate site title/logo presence
const siteTitle = page.locator('.site-title');
await expect(siteTitle).toBeVisible();
});
test('should navigate to the blog section', async ({ page }) => {
await page.goto('/');
await page.click('text=Blog');
// Validate URL and header
await expect(page).toHaveURL(/\/blog/);
await expect(page.locator('h1')).toContainText('Latest Posts');
});
});
Example: Admin Authentication
This test validates the login interface and access to the WordPress dashboard.
import { test, expect } from '@playwright/test';
test('Admin can log in successfully', async ({ page }) => {
await page.goto('/wp-login.php');
await page.fill('#user_login', process.env.WP_ADMIN_USER);
await page.fill('#user_pass', process.env.WP_ADMIN_PASSWORD);
await page.click('#wp-submit');
// Verify dashboard access
await expect(page).toHaveURL(/\/wp-admin/);
await expect(page.locator('.wrap h1')).toContainText('Dashboard');
});
Best Practices for UI Components
- Locators: Use user-facing attributes like
roleortextrather than CSS selectors where possible (e.g.,page.getByRole('button', { name: 'Submit' })). - Wait States: Avoid hardcoded timeouts. Playwright automatically waits for elements to be actionable, but for dynamic content, use
expect(locator).toBeVisible(). - Clean State: Each test should ideally start with a fresh browser context to ensure isolation.