# Playwright E2E 테스트 가이드 ## 스택 - Playwright + TypeScript - 페이지 오브젝트 패턴 (POM) - CI 통합 (GitHub Actions) --- ## 폴더 구조 ``` e2e/ ├── tests/ │ ├── auth.spec.ts # 인증 시나리오 │ ├── users.spec.ts # 사용자 관련 시나리오 │ └── ... ├── pages/ # 페이지 오브젝트 (POM) │ ├── LoginPage.ts │ ├── HomePage.ts │ └── BasePage.ts ├── fixtures/ # 테스트 픽스처 및 헬퍼 │ ├── auth.fixture.ts # 로그인 상태 픽스처 │ └── data.ts # 테스트 데이터 └── playwright.config.ts ``` --- ## 설정 ```typescript // playwright.config.ts import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests', fullyParallel: true, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: 'html', use: { baseURL: process.env.BASE_URL || 'http://localhost:5173', trace: 'on-first-retry', screenshot: 'only-on-failure', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, { name: 'Mobile Chrome', use: { ...devices['Pixel 5'] } }, ], webServer: { command: 'npm run dev', url: 'http://localhost:5173', reuseExistingServer: !process.env.CI, }, }); ``` --- ## 페이지 오브젝트 패턴 ```typescript // pages/BasePage.ts export class BasePage { constructor(protected page: Page) {} async goto(path: string) { await this.page.goto(path); } async waitForLoaded() { await this.page.waitForLoadState('networkidle'); } } // pages/LoginPage.ts export class LoginPage extends BasePage { readonly emailInput = this.page.getByLabel('Email'); readonly passwordInput = this.page.getByLabel('Password'); readonly submitButton = this.page.getByRole('button', { name: 'Login' }); readonly errorMessage = this.page.getByRole('alert'); async login(email: string, password: string) { await this.goto('/login'); await this.emailInput.fill(email); await this.passwordInput.fill(password); await this.submitButton.click(); } } ``` --- ## 테스트 픽스처 (로그인 상태 재사용) ```typescript // fixtures/auth.fixture.ts type AuthFixtures = { authenticatedPage: Page; loginPage: LoginPage; }; export const test = base.extend({ authenticatedPage: async ({ page }, use) => { // API로 직접 로그인 (UI 우회 — 빠른 셋업) const response = await page.request.post('/api/v1/auth/login', { data: { email: 'test@example.com', password: 'password123' } }); const { token } = await response.json(); await page.context().addCookies([{ name: 'token', value: token, url: 'http://localhost:5173' }]); await use(page); }, loginPage: async ({ page }, use) => { await use(new LoginPage(page)); }, }); export { expect } from '@playwright/test'; ``` --- ## 테스트 작성 패턴 ```typescript // tests/auth.spec.ts import { test, expect } from '../fixtures/auth.fixture'; import { LoginPage } from '../pages/LoginPage'; test.describe('인증', () => { test('유효한 자격증명으로 로그인 성공', async ({ page }) => { const loginPage = new LoginPage(page); await loginPage.login('user@example.com', 'password123'); await expect(page).toHaveURL('/home'); await expect(page.getByText('Welcome')).toBeVisible(); }); test('잘못된 비밀번호로 로그인 실패', async ({ page }) => { const loginPage = new LoginPage(page); await loginPage.login('user@example.com', 'wrong'); await expect(loginPage.errorMessage).toContainText('Invalid credentials'); await expect(page).toHaveURL('/login'); }); test('로그인한 사용자는 대시보드 접근 가능', async ({ authenticatedPage }) => { await authenticatedPage.goto('/dashboard'); await expect(authenticatedPage).toHaveURL('/dashboard'); }); }); ``` --- ## API 모킹 (선택적) ```typescript // 외부 API 모킹 (백엔드 불안정 시) test('결제 성공 시나리오', async ({ page }) => { await page.route('/api/v1/payments', (route) => { route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ success: true, orderId: 'TEST-001' }), }); }); // 테스트 진행... }); ``` --- ## CI 통합 (GitHub Actions) ```yaml # .github/workflows/e2e.yml name: E2E Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 20 } - run: npm ci - run: npx playwright install --with-deps chromium - run: npx playwright test env: BASE_URL: http://localhost:5173 - uses: actions/upload-artifact@v4 if: failure() with: name: playwright-report path: playwright-report/ ``` --- ## 실행 명령 ```bash # 전체 테스트 npx playwright test # 특정 파일 npx playwright test auth.spec.ts # UI 모드 (인터랙티브) npx playwright test --ui # 리포트 열기 npx playwright show-report ```