Playwright - E2E Tesztelés
A Playwright a Microsoft által fejlesztett modern end-to-end tesztelési keretrendszer. Támogatja a Chromium, Firefox és WebKit böngészőket, és kiválóan alkalmas WordPress oldalak és admin felületek automatizált tesztelésére.
Miért Playwright?
- Több böngésző - Chromium, Firefox, WebKit támogatás
- Auto-wait - Automatikus várakozás elemekre
- Gyors - Párhuzamos teszt futtatás
- Megbízható - Stabil szelektorok és retry logika
- Trace viewer - Vizuális debug eszköz
- WordPress kompatibilis - Gutenberg teszteléshez is használják
Telepítés
npm
npm init playwright@latest
Meglévő projektbe
npm install -D @playwright/test
npx playwright install
Projekt struktúra
my-project/
├── tests/
│ └── e2e/
│ ├── login.spec.ts
│ └── checkout.spec.ts
├── playwright.config.ts
└── package.json
Konfiguráció
playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
// Timeout beállítások
timeout: 30000,
expect: {
timeout: 5000,
},
// Párhuzamos futtatás
fullyParallel: true,
workers: process.env.CI ? 1 : undefined,
// Reporter
reporter: [
['html', { open: 'never' }],
['list'],
],
// Globális beállítások
use: {
baseURL: 'http://localhost:8080',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
// Böngésző konfigurációk
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
// Mobil tesztelés
{
name: 'mobile-chrome',
use: { ...devices['Pixel 5'] },
},
{
name: 'mobile-safari',
use: { ...devices['iPhone 12'] },
},
],
// Dev server indítása tesztek előtt
webServer: {
command: 'npm run dev',
url: 'http://localhost:8080',
reuseExistingServer: !process.env.CI,
},
});
Alapvető tesztek
Első teszt
import { test, expect } from '@playwright/test';
test('homepage has title', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveTitle(/WordPress/);
});
test('homepage has main content', async ({ page }) => {
await page.goto('/');
await expect(page.locator('main')).toBeVisible();
});
Navigáció és kattintás
test('navigation works', async ({ page }) => {
await page.goto('/');
// Link kattintás
await page.click('text=Blog');
// URL ellenőrzés
await expect(page).toHaveURL(/.*blog/);
// Tartalom ellenőrzés
await expect(page.locator('h1')).toContainText('Blog');
});
WordPress Admin tesztelés
Login teszt
import { test, expect } from '@playwright/test';
test('admin login', async ({ page }) => {
await page.goto('/wp-admin');
// Login form kitöltése
await page.fill('#user_login', 'admin');
await page.fill('#user_pass', 'password');
await page.click('#wp-submit');
// Dashboard ellenőrzése
await expect(page).toHaveURL(/.*wp-admin/);
await expect(page.locator('#wpbody-content h1')).toContainText('Dashboard');
});
Auth state mentése
playwright.config.ts:
export default defineConfig({
projects: [
// Setup projekt - bejelentkezés
{
name: 'setup',
testMatch: /.*\.setup\.ts/,
},
// Tesztek setup után
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/user.json',
},
dependencies: ['setup'],
},
],
});
tests/auth.setup.ts:
import { test as setup, expect } from '@playwright/test';
const authFile = 'playwright/.auth/user.json';
setup('authenticate', async ({ page }) => {
await page.goto('/wp-login.php');
await page.fill('#user_login', 'admin');
await page.fill('#user_pass', 'password');
await page.click('#wp-submit');
// Várakozás a dashboard-ra
await page.waitForURL('**/wp-admin/**');
// Auth state mentése
await page.context().storageState({ path: authFile });
});
Dashboard teszt (bejelentkezett állapotban)
import { test, expect } from '@playwright/test';
test('can access dashboard', async ({ page }) => {
// Már bejelentkezve van (auth state)
await page.goto('/wp-admin');
await expect(page.locator('#wpbody-content h1')).toContainText('Dashboard');
});
test('can create new post', async ({ page }) => {
await page.goto('/wp-admin/post-new.php');
// Gutenberg editor betöltésének várakozása
await page.waitForSelector('.block-editor');
// Cím megadása
await page.click('.editor-post-title__input');
await page.keyboard.type('Test Post Title');
// Tartalom hozzáadása
await page.click('.block-editor-default-block-appender__content');
await page.keyboard.type('This is test content.');
// Publish
await page.click('button:has-text("Publish")');
await page.click('.editor-post-publish-panel button:has-text("Publish")');
// Ellenőrzés
await expect(page.locator('.components-snackbar')).toContainText('published');
});
Szelektorok
Szöveges szelektorok
// Pontos szöveg
await page.click('text=Bejelentkezés');
// Részleges szöveg
await page.click('text=/belép/i');
// Szöveget tartalmazó elem
await page.locator('button', { hasText: 'Submit' }).click();
CSS szelektorok
// ID
await page.click('#submit-button');
// Class
await page.click('.btn-primary');
// Attribútum
await page.click('[data-testid="login-button"]');
// Kombinált
await page.click('button.btn-primary[type="submit"]');
XPath
await page.click('//button[@type="submit"]');
await page.click('xpath=//div[@class="content"]//a');
Pseudo szelektorok
// nth element
await page.click('.item >> nth=0');
// has text
await page.click('div:has-text("Hello")');
// has child
await page.click('article:has(img)');
// visible
await page.click('button:visible');
Form kezelés
Input kitöltése
test('form submission', async ({ page }) => {
await page.goto('/contact');
// Text input
await page.fill('input[name="name"]', 'John Doe');
await page.fill('input[name="email"]', '[email protected]');
// Textarea
await page.fill('textarea[name="message"]', 'Hello World!');
// Select
await page.selectOption('select[name="subject"]', 'support');
// Checkbox
await page.check('input[name="newsletter"]');
// Radio
await page.check('input[value="option1"]');
// Submit
await page.click('button[type="submit"]');
// Sikeres küldés ellenőrzése
await expect(page.locator('.success-message')).toBeVisible();
});
File feltöltés
test('file upload', async ({ page }) => {
await page.goto('/wp-admin/upload.php?page=media-new');
// File input
await page.setInputFiles('input[type="file"]', 'tests/fixtures/image.jpg');
// Többszörös fájl
await page.setInputFiles('input[type="file"]', [
'tests/fixtures/image1.jpg',
'tests/fixtures/image2.jpg',
]);
// Várakozás a feltöltésre
await expect(page.locator('.media-item')).toBeVisible();
});
Assertions
Page assertions
await expect(page).toHaveTitle('My Site');
await expect(page).toHaveURL(/.*\/blog/);
await expect(page).toHaveURL('http://localhost/blog');
Element assertions
const element = page.locator('.my-element');
await expect(element).toBeVisible();
await expect(element).toBeHidden();
await expect(element).toBeEnabled();
await expect(element).toBeDisabled();
await expect(element).toBeChecked();
await expect(element).toHaveText('Hello');
await expect(element).toContainText('Hello');
await expect(element).toHaveAttribute('href', '/about');
await expect(element).toHaveClass(/active/);
await expect(element).toHaveCount(3);
await expect(element).toHaveValue('John');
Soft assertions
test('soft assertions', async ({ page }) => {
await page.goto('/');
// Nem áll le hiba esetén
await expect.soft(page.locator('.hero')).toBeVisible();
await expect.soft(page.locator('.sidebar')).toBeVisible();
// Összesített eredmény
expect(test.info().errors).toHaveLength(0);
});
Várakozások
Explicit várakozások
// Elem megjelenésére
await page.waitForSelector('.loaded');
// Elem eltűnésére
await page.waitForSelector('.spinner', { state: 'hidden' });
// URL-re
await page.waitForURL('**/success');
// Hálózati idle-re
await page.waitForLoadState('networkidle');
// Timeout
await page.waitForTimeout(1000);
Response várakozás
test('wait for API', async ({ page }) => {
await page.goto('/');
// API válaszra várakozás
const responsePromise = page.waitForResponse('**/api/data');
await page.click('button.load-data');
const response = await responsePromise;
expect(response.status()).toBe(200);
});
WooCommerce tesztelés
Termék vásárlás
test('complete checkout', async ({ page }) => {
// Termék oldal
await page.goto('/product/test-product');
await page.click('button.add_to_cart_button');
// Kosár
await page.goto('/cart');
await expect(page.locator('.cart_item')).toHaveCount(1);
// Checkout
await page.click('a.checkout-button');
// Billing adatok
await page.fill('#billing_first_name', 'John');
await page.fill('#billing_last_name', 'Doe');
await page.fill('#billing_address_1', '123 Main St');
await page.fill('#billing_city', 'Budapest');
await page.fill('#billing_postcode', '1234');
await page.fill('#billing_phone', '+36301234567');
await page.fill('#billing_email', '[email protected]');
// Fizetési mód
await page.check('#payment_method_cod');
// Rendelés elküldése
await page.click('#place_order');
// Sikeres rendelés
await expect(page.locator('.woocommerce-order-received')).toBeVisible();
});
Page Object Model
Page objektum
// tests/pages/LoginPage.ts
import { Page, Locator } from '@playwright/test';
export class LoginPage {
readonly page: Page;
readonly usernameInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
constructor(page: Page) {
this.page = page;
this.usernameInput = page.locator('#user_login');
this.passwordInput = page.locator('#user_pass');
this.submitButton = page.locator('#wp-submit');
}
async goto() {
await this.page.goto('/wp-login.php');
}
async login(username: string, password: string) {
await this.usernameInput.fill(username);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
}
Page objektum használata
// tests/e2e/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
test('can login', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('admin', 'password');
await expect(page).toHaveURL(/.*wp-admin/);
});
Screenshots és videók
Screenshot készítése
test('visual test', async ({ page }) => {
await page.goto('/');
// Teljes oldal screenshot
await page.screenshot({ path: 'screenshots/homepage.png', fullPage: true });
// Elem screenshot
await page.locator('.hero').screenshot({ path: 'screenshots/hero.png' });
});
Visual regression testing
test('visual comparison', async ({ page }) => {
await page.goto('/');
// Összehasonlítás mentett screenshot-tal
await expect(page).toHaveScreenshot('homepage.png');
// Elem összehasonlítás
await expect(page.locator('.hero')).toHaveScreenshot('hero.png');
});
Debug és Trace
Debug mód
# Debug UI megnyitása
npx playwright test --debug
# Egy teszt debug-olása
npx playwright test login.spec.ts --debug
Trace viewer
# Trace engedélyezése
npx playwright test --trace on
# Trace megtekintése
npx playwright show-trace trace.zip
Console logok
test('debug logs', async ({ page }) => {
// Console logok figyelése
page.on('console', (msg) => {
console.log(`Browser log: ${msg.text()}`);
});
await page.goto('/');
});
CI/CD integráció
GitHub Actions
name: Playwright Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Start WordPress
run: |
docker-compose up -d
npm run wait-for-wordpress
- name: Run tests
run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
Hasznos parancsok
# Összes teszt futtatása
npx playwright test
# Specifikus fájl
npx playwright test login.spec.ts
# Specifikus teszt
npx playwright test -g "can login"
# Böngésző megadása
npx playwright test --project=chromium
# Headed mód (böngésző látszik)
npx playwright test --headed
# UI mód
npx playwright test --ui
# Report megnyitása
npx playwright show-report