Skip to main content

Tìm hiểu về Playwright Config

Cách sử dụng file playwright.config.(ts|js)

Hướng dẫn Cấu hình Playwright (playwright.config.ts)

📚 Mục lục

  1. Giới thiệu
  2. Cấu hình cơ bản
  3. Cấu hình Projects
  4. Global Setup & Teardown
  5. Cấu hình Use Options
  6. Recording Options
  7. Network Options
  8. Emulation Options
  9. Ví dụ hoàn chỉnh

Giới thiệu

File playwright.config.ts (hoặc .js) là nơi cấu hình toàn bộ test runner của Playwright. Đây là file quan trọng nhất để điều khiển cách thức chạy test của bạn.

Cấu trúc cơ bản

import { defineConfig } from '@playwright/test';

export default defineConfig({
// Cấu hình test runner ở đây (top-level)
testDir: 'tests',
timeout: 30000,

use: {
// Cấu hình browser/context ở đây
baseURL: 'http://localhost:3000',
},

projects: [
// Cấu hình các project/browser khác nhau
],
});

⚠️ Lưu ý quan trọng: Các tùy chọn test runner đặt ở top-level, KHÔNG đặt trong section use.


Cấu hình cơ bản

1. Thư mục test và file patterns

export default defineConfig({
// Thư mục chứa file test
testDir: 'tests',

// Pattern để tìm file test (mặc định: *.spec.ts, *.test.ts)
testMatch: '*todo-tests/*.spec.ts',

// Pattern để bỏ qua file test
testIgnore: '*test-assets',
});

📌 Giải thích:

  • testDir: Thư mục gốc chứa các file test
  • testMatch: Chỉ chạy các file khớp với pattern này
  • testIgnore: Bỏ qua các file khớp với pattern này

2. Timeout và Retries

export default defineConfig({
// Timeout cho mỗi test (mặc định: 30 giây)
timeout: 30000,

// Số lần retry khi test fail (0 = không retry)
retries: process.env.CI ? 2 : 0,

expect: {
// Timeout cho các assertion (mặc định: 5 giây)
timeout: 5000,
},
});

💡 Best Practice:

  • Trên CI: nên set retries: 2 để tránh false negative
  • Local development: retries: 0 để phát hiện vấn đề sớm

3. Parallel và Workers

export default defineConfig({
// Chạy tất cả test song song
fullyParallel: true,

// Số worker process (default: 50% số CPU cores)
workers: process.env.CI ? 1 : undefined,

// Ngăn test.only trên CI
forbidOnly: !!process.env.CI,
});

🚀 Performance Tips:

  • Local: để workers: undefined (sẽ dùng 50% cores)
  • CI: set workers: 1 nếu CI server yếu, hoặc 2-4 nếu mạnh

4. Reporter

export default defineConfig({
// Reporter để hiển thị kết quả test
reporter: [
['html'], // HTML report
['list'], // Console output
['json', { outputFile: 'test-results.json' }],
],

// Thư mục lưu kết quả test
outputDir: 'test-results',
});

📊 Reporter phổ biến:

  • html: Báo cáo HTML đẹp mắt (khuyên dùng)
  • list: In ra console, phù hợp cho CI
  • dot: Compact format
  • json: Export JSON để xử lý tiếp
  • junit: Tích hợp với Jenkins/CI tools

Cấu hình Projects

Projects cho phép chạy test trên nhiều browser/môi trường khác nhau.

Ví dụ: Multi-browser testing

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
projects: [
// Desktop Chrome
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},

// Desktop Firefox
{
name: 'firefox',
use: {
...devices['Desktop Firefox'],
},
},

// Desktop Safari
{
name: 'webkit',
use: {
...devices['Desktop Safari'],
},
},

// Mobile Chrome
{
name: 'Mobile Chrome',
use: {
...devices['Pixel 5'],
},
},

// Mobile Safari
{
name: 'Mobile Safari',
use: {
...devices['iPhone 12'],
},
},
],
});

Project với cấu hình riêng

projects: [
{
name: 'staging',
use: {
baseURL: 'https://staging.example.com',
screenshot: 'only-on-failure',
},
},
{
name: 'production',
use: {
baseURL: 'https://example.com',
screenshot: 'off',
},
},
],

🎯 Chạy project cụ thể:

# Chỉ chạy trên Chrome
npx playwright test --project=chromium

# Chạy nhiều project
npx playwright test --project=chromium --project=firefox

Global Setup & Teardown

Khi nào cần dùng?

  • Khởi tạo database trước khi test
  • Login và lưu authentication state
  • Start/stop services
  • Cleanup sau khi test xong

Cấu hình

export default defineConfig({
// File setup chạy TRƯỚC tất cả test
globalSetup: require.resolve('./global-setup.ts'),

// File teardown chạy SAU tất cả test
globalTeardown: require.resolve('./global-teardown.ts'),
});

Ví dụ: Global Setup

global-setup.ts:

import { chromium, FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
console.log('🚀 Starting global setup...');

// 1. Khởi tạo database
await setupDatabase();

// 2. Login và lưu auth state
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/login');
await page.fill('input[name="username"]', 'admin');
await page.fill('input[name="password"]', 'password123');
await page.click('button[type="submit"]');

// Lưu storage state
await page.context().storageState({
path: 'auth-state.json'
});

await browser.close();

console.log('✅ Global setup completed');
}

export default globalSetup;

global-teardown.ts:

import { FullConfig } from '@playwright/test';

async function globalTeardown(config: FullConfig) {
console.log('🧹 Starting cleanup...');

// Cleanup database, services, etc.
await cleanupDatabase();

console.log('✅ Cleanup completed');
}

export default globalTeardown;

Sử dụng Auth State trong test

export default defineConfig({
use: {
// Tất cả test sẽ dùng auth state này
storageState: 'auth-state.json',
},
});

Cấu hình Use Options

Section use chứa các options áp dụng cho browser và context.

Base URL

use: {
// Base URL cho tất cả các page.goto()
baseURL: 'http://localhost:3000',
},

Sử dụng trong test:

// Thay vì:
await page.goto('http://localhost:3000/products');

// Chỉ cần:
await page.goto('/products');

Action Timeout

use: {
// Timeout cho mỗi action (click, fill, v.v.)
actionTimeout: 10000, // 10 giây
},

Browser Options

use: {
// Chọn browser
browserName: 'chromium', // hoặc 'firefox', 'webkit'

// Channel (Chrome/Edge variants)
channel: 'chrome', // 'chrome', 'msedge', 'chrome-beta'

// Chạy browser có UI (không headless)
headless: false,

// Bypass CSP
bypassCSP: true,
},

Test ID Attribute

use: {
// Thay đổi attribute mặc định từ data-testid
testIdAttribute: 'data-test-id',
},

Sử dụng trong test:

// HTML: <button data-test-id="submit-btn">Submit</button>
await page.getByTestId('submit-btn').click();

Recording Options

Ghi lại screenshots, videos và traces để debug.

Cấu hình Screenshot

use: {
screenshot: 'only-on-failure',
// Options: 'off' | 'on' | 'only-on-failure'
},

📸 Screenshot modes:

  • off: Không chụp screenshot
  • on: Chụp screenshot sau mỗi test
  • only-on-failure: Chỉ chụp khi test fail ✅ Recommended

Cấu hình Video

use: {
video: 'retain-on-failure',
// Options: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry'
},

🎥 Video modes:

  • off: Không record video
  • on: Record tất cả test
  • retain-on-failure: Chỉ giữ video của test fail ✅ Recommended
  • on-first-retry: Chỉ record lần retry đầu tiên

Cấu hình Trace

use: {
trace: 'on-first-retry',
// Options: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry'
},

🔍 Trace modes:

  • off: Không thu thập trace
  • on: Thu thập trace cho tất cả test
  • retain-on-failure: Chỉ giữ trace của test fail
  • on-first-retry: Chỉ thu thập khi retry ✅ Recommended

Xem trace:

npx playwright show-trace trace.zip

Cấu hình Visual Comparison

expect: {
toHaveScreenshot: {
// Số pixel khác biệt tối đa
maxDiffPixels: 100,
},

toMatchSnapshot: {
// Tỷ lệ % pixel khác biệt tối đa
maxDiffPixelRatio: 0.1, // 10%
},
},

Ví dụ config hoàn chỉnh cho Recording

export default defineConfig({
use: {
// Screenshot khi fail
screenshot: 'only-on-failure',

// Video khi fail
video: 'retain-on-failure',

// Trace khi retry
trace: 'on-first-retry',
},

// Thư mục lưu artifacts
outputDir: 'test-results',

expect: {
toHaveScreenshot: {
maxDiffPixels: 100,
},
},
});

Network Options

Accept Downloads

use: {
// Tự động tải file về
acceptDownloads: true,
},

HTTP Headers

use: {
// Header gửi kèm mọi request
extraHTTPHeaders: {
'X-API-Key': 'your-api-key',
'Accept-Language': 'vi-VN',
},
},

HTTP Authentication

use: {
// Basic authentication
httpCredentials: {
username: 'admin',
password: 'secret123',
},
},

Ignore HTTPS Errors

use: {
// Bỏ qua lỗi SSL certificate
ignoreHTTPSErrors: true,
},

Offline Mode

use: {
// Emulate network offline
offline: true,
},

Proxy

use: {
proxy: {
server: 'http://myproxy.com:3128',
bypass: 'localhost,*.example.com',
username: 'user',
password: 'pass',
},
},

Emulation Options

Viewport

use: {
// Kích thước màn hình
viewport: { width: 1280, height: 720 },

// Hoặc null để dùng kích thước thật
viewport: null,
},

User Agent

use: {
userAgent: 'My Custom User Agent',
},

Geolocation

use: {
// Vị trí địa lý
geolocation: {
longitude: 106.6297,
latitude: 10.8231
}, // Tọa độ TP.HCM

// Cần grant permission
permissions: ['geolocation'],
},

Locale & Timezone

use: {
// Ngôn ngữ/vùng
locale: 'vi-VN',

// Múi giờ
timezoneId: 'Asia/Ho_Chi_Minh',
},

Color Scheme

use: {
// Dark mode / Light mode
colorScheme: 'dark', // 'light' | 'dark'
},

Permissions

use: {
// Cấp quyền cho browser
permissions: ['geolocation', 'notifications', 'camera'],
},

Device Emulation

import { devices } from '@playwright/test';

use: {
// Emulate iPhone 12
...devices['iPhone 12'],

// Hoặc custom
viewport: { width: 375, height: 667 },
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0...)',
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
},

Ví dụ hoàn chỉnh

Config cho dự án thực tế

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
// ============ TEST RUNNER CONFIG ============
testDir: './tests',
testMatch: '**/*.spec.ts',
testIgnore: '**/node_modules/**',

// Timeout
timeout: 30000,

// Parallel
fullyParallel: true,
workers: process.env.CI ? 2 : undefined,

// Retries
retries: process.env.CI ? 2 : 0,

// CI
forbidOnly: !!process.env.CI,

// Reporter
reporter: [
['html', { open: 'never' }],
['list'],
['json', { outputFile: 'test-results.json' }],
],

// Output
outputDir: 'test-results',

// Expect
expect: {
timeout: 5000,
toHaveScreenshot: {
maxDiffPixels: 100,
},
},

// ============ USE OPTIONS ============
use: {
// Base URL
baseURL: process.env.BASE_URL || 'http://localhost:3000',

// Action timeout
actionTimeout: 10000,

// Recording
screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'on-first-retry',

// Network
extraHTTPHeaders: {
'Accept-Language': 'vi-VN',
},
ignoreHTTPSErrors: true,

// Emulation
locale: 'vi-VN',
timezoneId: 'Asia/Ho_Chi_Minh',
viewport: { width: 1280, height: 720 },

// Storage state (auth)
storageState: 'auth-state.json',
},

// ============ PROJECTS ============
projects: [
// Setup: Login trước
{
name: 'setup',
testMatch: '**/*.setup.ts',
},

// Desktop Chrome
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
dependencies: ['setup'],
},

// Desktop Firefox
{
name: 'firefox',
use: {
...devices['Desktop Firefox'],
},
dependencies: ['setup'],
},

// Mobile Chrome
{
name: 'mobile-chrome',
use: {
...devices['Pixel 5'],
},
dependencies: ['setup'],
},

// API Testing (không cần browser)
{
name: 'api',
testMatch: '**/*.api.spec.ts',
use: {
baseURL: process.env.API_URL || 'http://localhost:3001',
},
},
],

// ============ GLOBAL SETUP ============
globalSetup: require.resolve('./global-setup.ts'),
globalTeardown: require.resolve('./global-teardown.ts'),

// ============ WEB SERVER ============
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120000,
},
});

📋 Tổng kết

Checklist cho Config tốt

  • ✅ Cấu hình retries cho CI
  • ✅ Sử dụng fullyParallel để tăng tốc
  • ✅ Bật recording (screenshot, video, trace) với mode thông minh
  • ✅ Cấu hình baseURL để code ngắn gọn
  • ✅ Setup projects cho multi-browser testing
  • ✅ Dùng globalSetup cho authentication
  • ✅ Cấu hình webServer nếu cần start dev server
  • ✅ Set forbidOnly: true trên CI

Câu lệnh hữu ích

# Chạy tất cả test
npx playwright test

# Chạy test trên một project
npx playwright test --project=chromium

# Chạy test với UI mode
npx playwright test --ui

# Debug một test
npx playwright test --debug

# Xem HTML report
npx playwright show-report

# Xem trace
npx playwright show-trace trace.zip

Tài liệu tham khảo


🎉 Chúc bạn viết test thành công với Playwright!