ก่อนอ่านบทนี้ ลองตอบ:¶
test.slow()ต่างจากtest.fixme()อย่างไร — ทั้งสองตัวเมื่อรันแล้วเกิดอะไรขึ้น?- ถ้าต้องการรัน test เฉพาะกลุ่ม
@smokeด้วย--grepแต่ยกเว้น@slowด้วย ควรเขียน CLI command อย่างไร?
เฉลย:
test.slow()— รัน test ตามปกติ แต่ขยาย timeout เป็น 3 เท่า (ใช้เมื่อ test ต้องการเวลานาน แต่ยังทำงานได้ถูกต้อง) |test.fixme()— ไม่รัน test เลย เหมือน skip แต่มีความหมายว่า "มีแผนจะแก้" ต่างจากtest.skip()ที่ไม่มีแผนชัดเจนnpx playwright test --grep "@smoke" --grep-invert "@slow"—--grepfilter เฉพาะ pattern ที่ match,--grep-invertยกเว้น pattern ที่ match
บทที่ 10: Configuration & Projects — ปรับ Playwright ให้ทำงานตาม Environment¶
1. วัตถุประสงค์¶
หลังอ่านบทนี้คุณจะ:
- เข้าใจโครงสร้างของ
playwright.config.tsและอธิบายทุก option หลักได้ - ใช้ Projects เพื่อรัน test suite เดียวกันบนหลาย browser หรือหลาย environment พร้อมกัน
- ตั้งค่า timeout ทั้ง 7 ประเภท ได้อย่างถูกต้อง รู้ว่าแต่ละตัวควบคุมอะไร
- ใช้
webServerเพื่อให้ Playwright start development server อัตโนมัติก่อนรัน test - ใช้ Project Dependencies แทน
globalSetupแบบเก่า เพื่อทำ authentication setup - หลีกเลี่ยง common mistakes เช่น hardcode URL หรือตั้ง
trace: 'on'ใน production CI
2. ทำไมต้องรู้? (Why)¶
ลองนึกถึงชีวิตก่อนมี config ที่ดี:
- ทุก test file เขียน
page.goto('http://localhost:3000/login')แบบ hardcode — แล้ววันนึง URL เปลี่ยนเป็นstaging.company.comต้องไปแก้ทุกไฟล์ - CI pipeline ล้มเหลวเพราะ dev ลืม comment
test.onlyออก — แต่ไม่มี guard ป้องกัน - test รันบน Chrome เครื่องตัวเองผ่าน แต่ Firefox ที่ CI กลับ fail — ทั้งที่ควรรันทั้งสองตั้งแต่แรก
- Dev server ต้องรันก่อนด้วยมือในอีก terminal — ถ้าลืม test เปิด URL ไม่ได้
playwright.config.ts แก้ปัญหาเหล่านี้ทั้งหมดในที่เดียว: centralizing configuration แทนการ scatter ตาม test files
3. เนื้อหาหลัก¶
defineConfig() — Blueprint ของ Test Suite¶
// playwright.config.ts
// tested: Playwright v1.50+, Node.js 20+
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: false,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
});
มาแกะทีละ option ให้ชัดขึ้น:
fullyParallel vs workers — สองสิ่งที่สับสนบ่อยที่สุด
workers คือจำนวน parallel processes ที่ Playwright spawns — เหมือนจำนวน "คนงาน" ที่ทำงานพร้อมกัน
fullyParallel คือ "ทุก test ในทุก file รัน parallel ได้หรือไม่" — ถ้า fullyParallel: false (default) แต่ละ file รันทีละ test ตามลำดับ (sequential ภายใน file) แต่ files ต่างกันรัน parallel กันได้ ถ้า fullyParallel: true ทุก test ในทุก file รัน parallel พร้อมกันได้
workers: 4, fullyParallel: false → 4 files รันพร้อมกัน, แต่ละ file รัน tests ทีละตัว
workers: 4, fullyParallel: true → tests ทั้งหมดรัน parallel สูงสุดพร้อมกัน 4 test
process.env.CI คืออะไร?
process.env คือ object ใน Node.js ที่เก็บ environment variables — ตัวแปรที่ set ก่อนรัน program ระบบ CI (เช่น GitHub Actions, Jenkins) จะ set CI=true โดยอัตโนมัติ ทำให้ process.env.CI มีค่าเป็น "true" เมื่อรันบน CI และเป็น undefined เมื่อรันบนเครื่องตัวเอง
process.env.CI // → "true" ใน CI, undefined ใน local
process.env.CI ? 2 : 0 // → 2 ใน CI, 0 ใน local
!!process.env.CI // → true ใน CI, false ใน local
forbidOnly — Guard สำหรับ CI
ถ้า dev ลืม test.only ทิ้งไว้ใน code แล้ว push ขึ้น CI — Playwright จะรันแค่ test นั้นเดียว ทั้ง suite ผ่านเพราะรันแค่ test เดียว! forbidOnly: !!process.env.CI บังคับให้ exit error ถ้าเจอ .only ใน CI
retries — Best Practice สำหรับ CI
ทำไม 2 ใน CI แต่ 0 ใน local? เพราะ:
- CI environment มี network latency, resource contention, cold start — บางครั้ง test fail เพราะ infra ไม่ใช่ code retry 2 ครั้งช่วย catch flakiness จริงๆ
- Local development ถ้า test fail แล้ว retry pass ทันที = ปัญหาถูกซ่อน ดีกว่า fail ทันทีให้ dev แก้
หมายเหตุ: ตั้งแต่ Playwright v1.52+ มี failOnFlakyTests: !!process.env.CI ที่ทำให้ CI exit error ถ้า test pass เพราะ retry (แสดงว่า test นั้น flaky) — เป็น option เสริมที่ combine ได้กับ retries
Projects — รัน Test Suite เดียวบนหลาย Browser¶
// tested: Playwright v1.50+, Node.js 20+
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
// Mobile emulation
{
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'] },
},
{
name: 'Mobile Safari',
use: { ...devices['iPhone 12'] },
},
],
});
devices['Desktop Chrome'] เป็น preset ที่ Playwright จัดมาให้ ครอบคลุม viewport, userAgent, browser engine ที่ถูกต้องสำหรับแต่ละ device — ไม่ต้องตั้งค่าเอง
รัน project เฉพาะ:
Project Dependencies — Modern Global Setup
แทนที่จะใช้ globalSetup แบบเก่า (ที่ไม่ขึ้น HTML report, ไม่รองรับ trace) ให้ใช้ setup project แทน:
// tested: Playwright v1.50+, Node.js 20+
export default defineConfig({
projects: [
{
name: 'setup',
testMatch: /global\.setup\.ts/, // รัน setup ก่อน
},
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
dependencies: ['setup'], // รอให้ setup ผ่านก่อน
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
dependencies: ['setup'],
},
],
});
// tests/global.setup.ts
import { test as setup } from '@playwright/test';
setup('authenticate admin user', async ({ page }) => {
await page.goto('/login');
await page.fill('[data-testid="input-username"]', 'admin');
await page.fill('[data-testid="input-password"]', process.env.ADMIN_PASSWORD!);
await page.click('[data-testid="btn-login"]');
// บันทึก session state ให้ tests ใช้
await page.context().storageState({ path: 'playwright/.auth/admin.json' });
});
ข้อดีของ Project Dependencies เทียบ globalSetup เก่า:
- Setup ปรากฏใน HTML report แยกต่างหาก ดู debug ได้ง่าย
- Trace recording ทำงานได้ใน setup phase
- ใช้ fixtures ได้ (เช่น page, request) ไม่ต้องสร้าง browser เอง
Timeout — 7 ประเภทที่ต้องรู้¶
Playwright แยก timeout ละเอียดกว่า testing framework อื่น ทำให้ debug ง่ายขึ้นมาก เพราะรู้ว่า timeout ไหนเกิดที่ขั้นตอนไหน — ถ้าใน Robot Framework คุณเจอแค่ timeout ระดับเดียว ใน Playwright มีถึง 7 ระดับที่แต่ละตัวควบคุมส่วนที่แตกต่างกัน:
| Timeout | Default | ตั้งค่าที่ | ควบคุมอะไร |
|---|---|---|---|
| test timeout | 30,000 ms | timeout ใน config หรือ test.setTimeout() |
ทุกอย่างใน test รวม fixtures และ beforeEach |
| expect timeout | 5,000 ms | expect: { timeout } ใน config หรือ per-assertion { timeout } |
auto-retrying assertions |
| action timeout | ไม่มี default | use: { actionTimeout } ใน config หรือ per-action { timeout } |
แต่ละ action เช่น click(), fill() |
| navigation timeout | ไม่มี default | use: { navigationTimeout } ใน config หรือ per-nav { timeout } |
page.goto(), page.waitForNavigation() |
| global timeout | ไม่มี default | globalTimeout ใน config |
ทุก test รวมกัน (ทั้ง test run) |
| fixture timeout | ใช้ test timeout | { timeout } ใน fixture definition |
การ setup/teardown ของ fixture นั้นๆ |
| beforeAll/afterAll timeout | 30,000 ms | test.setTimeout() ภายใน hook |
การรัน hook นั้น |
การตั้งค่าใน config:
// tested: Playwright v1.50+, Node.js 20+
export default defineConfig({
timeout: 60_000, // test timeout: 60 วิ
globalTimeout: 3_600_000, // ทั้ง run ต้องจบใน 1 ชั่วโมง
expect: {
timeout: 10_000, // assertions retry ได้ถึง 10 วิ
},
use: {
actionTimeout: 15_000, // click/fill ต้องเสร็จใน 15 วิ
navigationTimeout: 30_000, // goto() ต้องเสร็จใน 30 วิ
},
});
Override เฉพาะ test:
test('test ที่ต้องใช้เวลานาน', async ({ page }) => {
test.setTimeout(120_000); // override เฉพาะ test นี้
// ...
});
test('test ที่ใช้ test.slow()', async ({ page }) => {
test.slow(); // ขยาย timeout เป็น 3 เท่าของ config timeout
// ...
});
webServer — Auto-start Development Server¶
// tested: Playwright v1.50+, Node.js 20+
export default defineConfig({
webServer: {
command: 'cd docs/playwright-typescript/playwright-course-app && npm start',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120_000, // รอ server start ได้ถึง 2 นาที (default 60 วิ)
stdout: 'pipe', // แสดง server logs ใน terminal
stderr: 'pipe',
},
});
reuseExistingServer: !process.env.CI หมายความว่า:
- Local: ถ้ามี server รันอยู่แล้ว ใช้ต่อได้เลย ไม่ต้อง start ใหม่ (ประหยัดเวลา)
- CI: ต้อง start server ใหม่ทุกครั้ง ห้าม reuse (เพื่อ clean state)
baseURL + Environment Variables¶
// playwright.config.ts
// tested: Playwright v1.50+, Node.js 20+
import 'dotenv/config';
export default defineConfig({
use: {
baseURL: process.env.BASE_URL ?? 'http://localhost:3000',
},
});
// test file — ใช้ relative path ได้เลย
test('login page loads', async ({ page }) => {
await page.goto('/login'); // ไปที่ baseURL + /login
await page.goto('/'); // ไปที่ baseURL
});
สร้าง .env สำหรับ local และ .env.staging สำหรับ staging:
# .env (local)
BASE_URL=http://localhost:3000
# .env.staging (staging)
BASE_URL=https://staging.company.com
tsconfig — Custom TypeScript Config สำหรับ Tests¶
Playwright v1.49+ รองรับ tsconfig property ใน config เพื่อระบุ tsconfig ที่ใช้กับ test files ทั้งหมด:
export default defineConfig({
tsconfig: './tsconfig.test.json', // ระบุ tsconfig เฉพาะสำหรับ tests
});
ถ้าไม่ระบุ Playwright จะ auto-detect tsconfig.json ที่ใกล้ที่สุดสำหรับแต่ละ imported file แยกกัน property นี้มีประโยชน์เมื่อต้องการ tsconfig เดียวที่ apply ทั้ง test suite
เปรียบเทียบกับ Robot Framework + Selenium¶
| ฟีเจอร์ | Robot Framework + Selenium | Playwright |
|---|---|---|
| Config file | robot.yaml, environment variables |
playwright.config.ts (TypeScript, type-safe) |
| Multi-browser | แยก process, รัน suite ซ้ำหลายรอบ | Projects ใน config เดียว, รันพร้อมกัน |
| Timeout | Set Global Timeout keyword, ระดับเดียว |
7 timeout types, granular ต่างระดับ |
| Dev server | รันแยกด้วยมือใน terminal | webServer auto-start, Playwright จัดการให้ |
| Retry | --retries CLI flag เท่านั้น |
retries ใน config + override ต่อ test ได้ |
| Global setup | ไม่มี built-in (ใช้ Suite Setup) | Project Dependencies (modern) หรือ globalSetup (legacy) |
| Environment config | robot.yaml + override ด้วย CLI | defineConfig() + process.env + dotenv |
4. ตัวอย่าง 3 ระดับ¶
Beginner: Minimal Config ที่ใช้งานได้จริง¶
สถานการณ์: เพิ่งเริ่มต้นโปรเจค ต้องการ config ที่ทำงานได้ทันที รัน tests บน Chrome เดียว
// playwright.config.ts
// tested: Playwright v1.50+, Node.js 20+
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
webServer: {
command: 'npm start',
url: 'http://localhost:3000',
reuseExistingServer: true,
},
});
// tests/home.spec.ts
// tested: Playwright v1.50+, Node.js 20+
import { test, expect } from '@playwright/test';
test('home page loads', async ({ page }) => {
await page.goto('/'); // ใช้ baseURL — ไม่ต้อง hardcode
await expect(page).toHaveTitle(/Playwright Course App/);
});
test('login page accessible', async ({ page }) => {
await page.goto('/login'); // relative path เสมอ
await expect(page.getByRole('heading', { name: 'Login' })).toBeVisible();
});
ผลลัพธ์เมื่อรัน npx playwright test:
Running 2 tests using 1 worker
✓ home page loads (543ms)
✓ login page accessible (312ms)
2 passed (2.1s)
Intermediate: Multi-Project Config ที่มี Setup Dependencies¶
สถานการณ์: ทีม QA ต้องรัน test บน 3 browsers พร้อมกัน แต่ทุก test ต้องการ admin session ก่อน — ใช้ setup project เพื่อทำ authentication ครั้งเดียว แล้วทุก browser project ใช้ auth state ร่วมกัน
// playwright.config.ts
// tested: Playwright v1.50+, Node.js 20+
import { defineConfig, devices } from '@playwright/test';
import 'dotenv/config';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 2 : undefined,
reporter: process.env.CI ? 'github' : 'html',
use: {
baseURL: process.env.BASE_URL ?? 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
// ── Setup project: รัน auth ก่อน ──────────────────────────────
{
name: 'setup',
testMatch: /global\.setup\.ts/,
},
// ── Browser projects: รอ setup ผ่านก่อน ───────────────────────
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/admin.json', // ใช้ auth state จาก setup
},
dependencies: ['setup'],
},
{
name: 'firefox',
use: {
...devices['Desktop Firefox'],
storageState: 'playwright/.auth/admin.json',
},
dependencies: ['setup'],
},
{
name: 'webkit',
use: {
...devices['Desktop Safari'],
storageState: 'playwright/.auth/admin.json',
},
dependencies: ['setup'],
},
],
webServer: {
command: 'cd docs/playwright-typescript/playwright-course-app && npm start',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
// tests/global.setup.ts
// tested: Playwright v1.50+, Node.js 20+
import { test as setup, expect } from '@playwright/test';
import path from 'path';
const authFile = path.join(__dirname, '../playwright/.auth/admin.json');
setup('authenticate as admin', async ({ page }) => {
await page.goto('/login');
await page.fill('[data-testid="input-username"]', 'admin');
await page.fill('[data-testid="input-password"]', process.env.ADMIN_PASSWORD ?? 'admin123');
await page.click('[data-testid="btn-login"]');
// ยืนยันว่า login สำเร็จก่อน save state — badge ต้องแสดง "Logged in as: admin"
await expect(page.getByTestId('session-badge')).toContainText('Logged in as: admin');
// บันทึก cookies + localStorage ให้ tests ใช้ต่อ
await page.context().storageState({ path: authFile });
});
// tests/admin-dashboard.spec.ts
// tested: Playwright v1.50+, Node.js 20+
import { test, expect } from '@playwright/test';
// test นี้ได้ storageState จาก project config — ไม่ต้อง login ใหม่
test('admin can see dashboard metrics', async ({ page }) => {
await page.goto('/admin');
await expect(page.getByRole('heading', { name: 'Admin Dashboard' })).toBeVisible();
await expect(page.getByTestId('stat-users')).toBeVisible();
});
เมื่อรัน npx playwright test:
Running 1 test using 1 worker
✓ [setup] › authenticate as admin (1.2s)
Running 3 tests using 3 workers
✓ [chromium] › admin-dashboard.spec.ts › admin can see dashboard metrics (543ms)
✓ [firefox] › admin-dashboard.spec.ts › admin can see dashboard metrics (621ms)
✓ [webkit] › admin-dashboard.spec.ts › admin can see dashboard metrics (498ms)
4 passed (3.8s)
Advanced: Diagnose Config ที่ทำให้ Test Flaky¶
สถานการณ์: CI report แสดงว่า test ที่ tests/checkout.spec.ts fail บน Firefox เป็นบางครั้ง error message คือ Test timeout of 30000ms exceeded แต่ local รันผ่านทุกครั้ง ต้องหาต้นเหตุและแก้ config
// playwright.config.ts (PROBLEMATIC — มีปัญหา 3 จุด)
// tested: Playwright v1.50+, Node.js 20+
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
timeout: 30_000, // ❌ จุดที่ 1: สำหรับ checkout flow ที่มี payment API นี้น้อยเกินไป
use: {
baseURL: 'http://localhost:3000',
trace: 'on', // ❌ จุดที่ 2: trace: 'on' ทุก test ทำให้ CI ช้า เพิ่ม overhead
actionTimeout: 0, // ❌ จุดที่ 3: ไม่มี action timeout = action ค้างได้ไม่จำกัด
},
projects: [
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
],
// ❌ ไม่มี retries ใน CI — ถ้า Firefox CI เจอ flakiness จะ fail ทันที
});
การวิเคราะห์:
timeout: 30_000สำหรับ checkout — checkout flow มี payment API call ที่อาจใช้เวลา 10-15 วินาที + assertion timeout 5 วินาที + navigation 5 วินาที รวมกันเกิน 30 วินาทีได้ใน CI ที่ช้ากว่า localtrace: 'on'— บันทึก trace ทุก test เพิ่ม I/O overhead ทำให้ทุก test ช้าขึ้น และ checkout ที่ใกล้ limit อยู่แล้วก็ timeoutactionTimeout: 0— ไม่มี action timeout คือรอไม่จำกัด แต่ขัดกับ test timeout 30 วินาที ถ้า action ค้างจะรอจนหมด test timeout แล้วค่อย fail — ทำให้ error message ดูเหมือน test timeout แต่จริงๆ เป็น action ค้าง
// playwright.config.ts (FIXED)
// tested: Playwright v1.50+, Node.js 20+
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
timeout: 60_000, // ✅ เพิ่มเป็น 60 วินาที รองรับ payment API ที่ช้า
retries: process.env.CI ? 2 : 0, // ✅ retry ใน CI จัดการ flakiness
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry', // ✅ บันทึกเฉพาะตอน retry — ไม่เปลือง I/O
actionTimeout: 15_000, // ✅ action ต้องเสร็จใน 15 วิ ถ้าไม่เสร็จ = มีปัญหาแน่นอน
navigationTimeout: 30_000,
},
projects: [
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
],
});
// tests/checkout.spec.ts
// tested: Playwright v1.50+, Node.js 20+
import { test, expect } from '@playwright/test';
// checkout flow ที่ใช้เวลานาน — override timeout เฉพาะ test นี้
test('complete checkout with payment', async ({ page }) => {
test.setTimeout(90_000); // override เฉพาะ test นี้ถ้าจำเป็น
await page.goto('/cart');
await page.click('[data-testid="btn-checkout"]');
// payment API อาจช้า — expect timeout จะ retry assertion ถึง 10 วินาที
await expect(page.getByTestId('order-success')).toBeVisible({
timeout: 20_000, // per-assertion override สำหรับ step นี้โดยเฉพาะ
});
});
ผลลัพธ์หลังแก้:
Running 1 test using 1 worker
✓ [firefox] › checkout.spec.ts › complete checkout with payment (12.3s)
1 passed (12.3s)
5. Common Mistakes¶
❌ Hardcode URL ใน test files แทนใช้ baseURL
// ❌ ผิด — แก้ URL ทีต้องไปแก้ทุกไฟล์
await page.goto('http://localhost:3000/login');
await page.goto('http://localhost:3000/dashboard');
// ✅ ถูก — ตั้ง baseURL ใน config ครั้งเดียว
// playwright.config.ts
use: { baseURL: 'http://localhost:3000' }
// test file
await page.goto('/login'); // relative path เสมอ
await page.goto('/todos');
❌ ใช้ trace: 'on' ใน config สำหรับ CI
// ✅ ถูก — บันทึก trace เฉพาะตอน retry ครั้งแรก (เพียงพอสำหรับ debug)
use: { trace: 'on-first-retry' }
❌ ไม่ตั้ง actionTimeout ทำให้ debug ยาก
// ❌ ผิด — ถ้า action ค้าง จะรอจนหมด test timeout แล้วได้ error message กว้างๆ
use: {
// ไม่มี actionTimeout
}
// ✅ ถูก — ตั้ง actionTimeout ทำให้รู้ทันทีว่า action ไหน timeout
use: {
actionTimeout: 15_000, // click/fill ต้องเสร็จใน 15 วิ
navigationTimeout: 30_000, // goto() ต้องเสร็จใน 30 วิ
}
❌ ใส่ generic type ใน defineConfig<PlaywrightTestConfig>()
// ❌ ผิด — ไม่จำเป็นและทำให้ code verbose
import { defineConfig } from '@playwright/test';
import type { PlaywrightTestConfig } from '@playwright/test';
export default defineConfig<PlaywrightTestConfig>({ ... });
// ✅ ถูก — defineConfig() infer types ให้อัตโนมัติ
import { defineConfig } from '@playwright/test';
export default defineConfig({ ... });
❌ ใช้ globalSetup แบบเก่าสำหรับ authentication
// ❌ เก่า — setup ไม่ขึ้น HTML report, ไม่มี trace, ต้องสร้าง browser เอง
export default defineConfig({
globalSetup: require.resolve('./global-setup'),
});
// ✅ Modern — ใช้ Project Dependencies แทน
export default defineConfig({
projects: [
{ name: 'setup', testMatch: /global\.setup\.ts/ },
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
dependencies: ['setup'],
},
],
});
6. สรุปบท¶
ลองตอบคำถามต่อไปนี้ก่อนดูเฉลย — เพื่อ consolidate ความเข้าใจ:
คำถาม 1: ถ้า config มี timeout: 30_000 และ actionTimeout: 15_000 แล้ว action หนึ่งใช้เวลา 20 วินาที — อะไรจะ timeout ก่อน และ error message จะบอกว่าอะไร?
คำถาม 2: ทีมมี tests 200 ไฟล์ และต้องการรันบน Chrome, Firefox, WebKit พร้อมกัน แต่มี setup project สำหรับ authentication ด้วย — เมื่อรัน npx playwright test Playwright จะรัน test กี่ครั้งโดยรวม (ไม่รวม setup)?
คำถาม 3: reuseExistingServer: !process.env.CI ทำงานอย่างไรใน local vs CI — อธิบายพฤติกรรมและเหตุผลที่แตกต่างกัน
ดูเฉลย
**เฉลย:** **คำถาม 1:** `actionTimeout: 15_000` จะ timeout ก่อน เพราะ action ใช้เวลา 20 วิ > 15 วิ Error message จะบอกว่า "action timeout of 15000ms exceeded" — ซึ่งบอกชัดกว่า "test timeout" ว่าปัญหาอยู่ที่ action นั้นโดยเฉพาะ ทำให้ debug ง่ายขึ้น **คำถาม 2:** 200 ไฟล์ × 3 projects = **600 ครั้ง** (ไม่รวม setup project ที่รัน 1 ครั้งก่อน) **คำถาม 3:** - **Local** (`!process.env.CI` = `true`): ถ้ามี server รันอยู่ที่ port 3000 แล้ว Playwright ใช้ต่อได้เลย ไม่ start ใหม่ ประหยัดเวลา dev ที่ต้องรัน test บ่อยๆ - **CI** (`!process.env.CI` = `false`): บังคับ start server ใหม่ทุกครั้ง เพื่อ guarantee clean state ป้องกัน test pass เพราะ state จาก run ก่อนหน้า7. Pre-chapter Retrieval สำหรับบทถัดไป¶
บทที่ 11 จะพูดถึง Parallelism, Sharding & Reporting — ลองนึกดูก่อน:
- ความแตกต่างระหว่าง
workersกับshardingคืออะไร — แต่ละอย่างแก้ปัญหาอะไร? - ถ้า test suite รัน 25 นาทีและมี 4 CI machines available คุณจะตั้งค่า sharding อย่างไร?