harness/plugins/zioinfo/skills/zio-harness/references/playwright.md
DESKTOP-TKLFCPR\ython 6caf43e1ed feat!: v2.0.0 — 4개 플러그인 zioinfo 단일 통합 + 최신 플러그인 기술 적용
- harness·zio-harness·proposal-builder·zioinfo → plugins/zioinfo (git mv 히스토리 보존)
- 스킬 4·커맨드 3(/zioinfo:pmo·proposal·wiki)·에이전트 15·graphify 훅·knowledge 통합
- 신규: /zioinfo:wiki (graphify LLM wiki — graphify-out/wiki/ 커뮤니티별 아티클)
- 신규: ZIO WISE 테마 (themes/zioinfo.json, experimental)
- manifest 최신화: $schema·displayName(ZIO INFOTECH Suite)·experimental.themes
- marketplace.json 단일 엔트리, 루트 plugin.json 제거
- CLAUDE.md·PROJECT_MAP·docs/plugins.md·README 3종·CHANGELOG·설치가이드 pptx 재구성

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 20:16:36 +09:00

220 lines
5.1 KiB
Markdown

# 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<AuthFixtures>({
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
```