Ugrás a fő tartalomhoz

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();
});
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

Források