e2e
E2E 명령어 (E2E Command)
이 명령어는 e2e-runner 에이전트를 호출하여 Playwright를 사용한 E2E(End-to-End) 테스트를 생성, 유지 관리 및 실행합니다.
이 명령어가 하는 일
- 테스트 여정 생성 - 사용자 흐름에 대한 Playwright 테스트 생성
- E2E 테스트 실행 - 여러 브라우저에서 테스트 실행
- 아티팩트 캡처 - 실패 시 스크린샷, 비디오, 트레이스 캡처
- 결과 업로드 - HTML 보고서 및 JUnit XML
- 불안정한(Flaky) 테스트 식별 - 불안정한 테스트 격리
사용 시점
다음 경우에 /e2e를 사용하세요:
- 핵심 사용자 여정 테스트 (로그인, 거래, 결제)
- 다단계 흐름이 끝까지 작동하는지 검증
- UI 상호작용 및 탐색 테스트
- 프론트엔드와 백엔드 간의 통합 검증
- 프로덕션 배포 준비
작동 방식
e2e-runner 에이전트는 다음을 수행합니다:
- 사용자 흐름 분석 및 테스트 시나리오 식별
- POM(Page Object Model) 패턴을 사용하여 Playwright 테스트 생성
- 여러 브라우저(Chrome, Firefox, Safari)에서 테스트 실행
- 스크린샷, 비디오, 트레이스로 실패 캡 처
- 결과 및 아티팩트와 함께 보고서 생성
- 불안정한 테스트 식별 및 수정 권장
사용 예시
User: /e2e 마켓 검색 및 조회 흐름을 테스트해 줘
Agent (e2e-runner):
# E2E 테스트 생성: 마켓 검색 및 조회 흐름
## 식별된 테스트 시나리오
**사용자 여정:** 마켓 검색 → 결과 보기 → 마켓 클릭 → 상세 정보 보기
**테 스트 커버리지:**
1. 마켓 페이지로 이동
2. 시맨틱 검색 수행
3. 검색 결과 검증
4. 첫 번째 결과 클릭
5. 마켓 상세 페이지 로드 검증
6. 차트 렌더링 검증
## 생성된 테스트 코드
```typescript
// tests/e2e/markets/search-and-view.spec.ts
import { test, expect } from '@playwright/test'
import { MarketsPage } from '../../pages/MarketsPage'
import { MarketDetailsPage } from '../../pages/MarketDetailsPage'
test.describe('Market Search and View Flow', () => {
test('user can search markets and view details', async ({ page }) => {
// 1. Navigate to markets page
const marketsPage = new MarketsPage(page)
await marketsPage.goto()
// Verify page loaded
await expect(page).toHaveTitle(/Markets/)
await expect(page.locator('h1')).toContainText('Markets')
// 2. Perform semantic search
await marketsPage.searchMarkets('election')
// Wait for API response
await page.waitForResponse(resp =>
resp.url().includes('/api/markets/search') && resp.status() === 200
)
// 3. Verify search results
const marketCards = marketsPage.marketCards
await expect(marketCards.first()).toBeVisible()
const resultCount = await marketCards.count()
expect(resultCount).toBeGreaterThan(0)
// Take screenshot of search results
await page.screenshot({ path: 'artifacts/search-results.png' })
// 4. Click on first result
const firstMarketTitle = await marketCards.first().textContent()
await marketCards.first().click()
// 5. Verify market details page loads
await expect(page).toHaveURL(/\/markets\/[a-z0-9-]+/)
const detailsPage = new MarketDetailsPage(page)
await expect(detailsPage.marketName).toBeVisible()
await expect(detailsPage.marketDescription).toBeVisible()
// 6. Verify chart renders
await expect(detailsPage.priceChart).toBeVisible()
// Verify market name matches
const detailsTitle = await detailsPage.marketName.textContent()
expect(detailsTitle?.toLowerCase()).toContain(
firstMarketTitle?.toLowerCase().substring(0, 20) || ''
)
// Take screenshot of market details
await page.screenshot({ path: 'artifacts/market-details.png' })
})
test('search with no results shows empty state', async ({ page }) => {
const marketsPage = new MarketsPage(page)
await marketsPage.goto()
// Search for non-existent market
await marketsPage.searchMarkets('xyznonexistentmarket123456')
// Verify empty state
await expect(page.locator('[data-testid="no-results"]')).toBeVisible()
await expect(page.locator('[data-testid="no-results"]')).toContainText(
/no.*results|no.*markets/i
)
const marketCount = await marketsPage.marketCards.count()
expect(marketCount).toBe(0)
})
test('can clear search and see all markets again', async ({ page }) => {
const marketsPage = new MarketsPage(page)
await marketsPage.goto()
// Initial market count
const initialCount = await marketsPage.marketCards.count()
// Perform search
await marketsPage.searchMarkets('trump')
await page.waitForLoadState('networkidle')
// Verify filtered results
const filteredCount = await marketsPage.marketCards.count()
expect(filteredCount).toBeLessThan(initialCount)
// Clear search
await marketsPage.searchInput.clear()
await page.waitForLoadState('networkidle')
// Verify all markets shown again
const finalCount = await marketsPage.marketCards.count()
expect(finalCount).toBe(initialCount)
})
})