요구사항 문서가 "지원한다"고 선언한 항목과 실제로 검증되는 범위가 어긋나 있었다.
지원 브라우저 3종 "최신 2개 버전" 선언에 비해 자동 테스트는 Chromium 단일이었고,
같은 형태의 불일치가 문서 전반에 있었다 — 디스크 700MB↔게이트 500MB, 확장 33개
필수 선언↔게이트 14개, HTTPS "필수"↔비차단 경고.
선언을 낮추는 대신 검증 수준을 명시하는 방향으로 정리했다. 항목마다 "설치 프로그램이
자동 검사" 와 "운영자 확인 필요" 를 구분하는 절(§9)을 두어, 이슈가 지적한 질문
("지원 선언이 어느 수준의 검증을 의미하는가")을 문서 전반으로 일반화했다.
조사 중 드러난 내부 하네스 결함 3건도 함께 고쳤다.
- 테스트 스코프 훅이 npm run test:e2e:smoke 를 오차단 — 필터가 package.json 안에
있어 명령 문자열에서 보이지 않았다. 회귀 케이스 3건 red 확인 후 수정
- 브라우저 테스트 안내 문서가 없어진 스크립트·디렉토리를 가리키고 있었다
- E2E 동반 강제 룰의 확장 config 경로 패턴이 실제 경로와 달라, 확장 8개의 config
변경이 인정되지 않았다
E2E 브라우저 확대는 하지 않는다 — 도입 조건만 문서에 못박았다.
190 lines
9.0 KiB
Markdown
190 lines
9.0 KiB
Markdown
# Playwright E2E (코어 영역)
|
||
|
||
G7 코어의 Playwright (TypeScript) 기반 결정론적 E2E 인프라.
|
||
|
||
## 빠른 시작
|
||
|
||
```powershell
|
||
# PowerShell (개발 환경)
|
||
$env:PLAYWRIGHT_BASE_URL='https://g7.dev'
|
||
npm run test:e2e # 전체 spec
|
||
npm run test:e2e:smoke # @smoke 태그 (--grep @smoke)
|
||
npm run test:e2e:ui # Playwright UI 모드 (디버깅)
|
||
|
||
# 특정 spec / 디렉토리만
|
||
npx playwright test tests/Playwright/specs/smoke/admin-base.spec.ts
|
||
npx playwright test tests/Playwright/specs/layout-editor --workers=2
|
||
```
|
||
|
||
```bash
|
||
# Bash (CI/Linux)
|
||
PLAYWRIGHT_BASE_URL=https://g7.dev npx playwright test
|
||
```
|
||
|
||
루트 `package.json` 이 제공하는 E2E 스크립트는 `test:e2e` · `test:e2e:smoke` · `test:e2e:ui`
|
||
3개다. 그 밖의 범위 지정은 `npx playwright test` 에 경로나 `--grep` 을 직접 준다.
|
||
|
||
브라우저는 Chromium 전용이다 (`playwright.config.ts` 의 `projects` 에 chromium 하나).
|
||
Firefox/WebKit 도입 조건은 [docs/testing/e2e-testing.md](../../docs/testing/e2e-testing.md)
|
||
§3 "브라우저 범위" 참조.
|
||
|
||
## Base URL 해석 우선순위 (하드코딩 회피)
|
||
|
||
`playwright.config.ts` 의 `resolveBaseUrl()` 가 다음 순서로 결정:
|
||
|
||
1. **`PLAYWRIGHT_BASE_URL` 환경변수** — CI / 명시적 오버라이드 (최우선)
|
||
2. **`.env` 의 `APP_URL`** — 단 `http://localhost` 류는 fallback 부적합 (Apache vhost 미경유)
|
||
3. **그 외** — 명시 에러 (의도적으로 base URL 을 지정하지 않으면 spec 미실행)
|
||
|
||
도메인이 가변(개발자/CI/운영 환경별 다름)이므로 fallback 하드코딩 금지.
|
||
|
||
## 디렉토리 구조
|
||
|
||
```text
|
||
tests/Playwright/
|
||
├── fixtures/
|
||
│ ├── auth.ts (Sanctum 토큰 발급 + addInitScript 주입)
|
||
│ ├── ecommerce-auth.ts (구매자 세션 fixture)
|
||
│ ├── layout-editor.ts (편집기 진입/저장 헬퍼)
|
||
│ ├── seed-layout.ts (시드 화면 준비/정리)
|
||
│ └── seed-layouts/ (시드 화면 원본 JSON)
|
||
├── global-setup.ts (시드 화면 설치)
|
||
├── global-teardown.ts (시드 화면 제거)
|
||
├── specs/
|
||
│ ├── smoke/ (핵심 화면 — 14 파일)
|
||
│ ├── admin/ (관리자 화면 — 17 파일)
|
||
│ ├── layout-editor/ (레이아웃 편집기 — 46 파일)
|
||
│ ├── pagination/ (목록 페이지네이션 — 4 파일)
|
||
│ ├── search/ (검색 — 1 파일)
|
||
│ └── *.spec.ts (엔진/렌더링 회귀 — 14 파일)
|
||
├── .auth/ (gitignore — storageState 캐시)
|
||
└── README.md (본 문서)
|
||
```
|
||
|
||
`@smoke` 표기는 70곳(25개 파일)이며, 그중 1곳은 `test.describe` 단위라 `--grep @smoke` 로
|
||
선택되는 테스트 수는 표기 수보다 많다. 파일·건수는 변하므로 판단이 필요할 때 직접 센다.
|
||
|
||
## 코어/확장 분리 원칙
|
||
|
||
기존 PHPUnit / Vitest 와 동일 원칙. **모듈/플러그인/템플릿 E2E spec 을 코어 디렉토리에 작성 금지.**
|
||
|
||
| 영역 | spec 위치 | config 위치 |
|
||
|---|---|---|
|
||
| **코어** | `tests/Playwright/specs/` | `playwright.config.ts` (루트) |
|
||
| 모듈 | `modules/_bundled/{id}/tests/Playwright/specs/` | 모듈 자체 `playwright.config.ts` |
|
||
| 플러그인 | `plugins/_bundled/{id}/tests/Playwright/specs/` | 플러그인 자체 |
|
||
| 템플릿 | `templates/_bundled/{id}/tests/Playwright/specs/` | 템플릿 자체 |
|
||
|
||
확장이 자체 E2E spec 을 추가하는 sample skeleton 은
|
||
`modules/_bundled/sirsoft-ecommerce/tests/Playwright/` 참조.
|
||
|
||
## 인증 가드 (PlaywrightIssueToken 3중 보안)
|
||
|
||
`app/Console/Commands/PlaywrightIssueToken.php` 는 다음 3중 가드를 강제한다:
|
||
|
||
1. **CLI 한정** — `php_sapi_name() === 'cli'` (production 웹 요청에서 절대 도달 불가)
|
||
2. **`G7_PLAYWRIGHT_BYPASS=1` 환경변수 옵트인** — `.env` 영구 수정 불요, 인라인 부착만으로 활성화
|
||
3. **`APP_DEBUG=true` inline override** — `production + debug=false` 환경에서도 토큰 발급 가능
|
||
|
||
호출은 `fixtures/auth.ts` 의 `issueToken()` 헬퍼가 자동으로 `G7_PLAYWRIGHT_BYPASS=1` 을 부착하므로
|
||
spec 작성자는 가드를 인지할 필요 없음.
|
||
|
||
```typescript
|
||
import { issueToken, authenticatePage } from '../../fixtures/auth';
|
||
|
||
test('...', async ({ page }) => {
|
||
const token = issueToken('core.templates.layouts.edit');
|
||
await authenticatePage(page, token);
|
||
await page.goto('/admin/dashboard');
|
||
});
|
||
```
|
||
|
||
권한 식별자는 임의 string — 코어/모듈/플러그인 공통 API. `Permission::firstOrCreate` 가 자동 생성.
|
||
|
||
## 데이터 생성 위치 — 책임 분리 매트릭스
|
||
|
||
E2E spec 이 "특정 유저 + 특정 역할 + 특정 도메인 상황" 에서 동작하려면 백엔드 데이터 생성 코드가
|
||
필요하다. **그 코드는 데이터를 소유한 영역에 위치해야 한다** — 기존 Seeder/Factory 분리 원칙과 동일.
|
||
|
||
| 데이터 종류 | 책임 영역 | 위치 |
|
||
|---|---|---|
|
||
| 코어 권한 (`core.*`), 역할, 유저, Sanctum 토큰 | 코어 | `app/Console/Commands/PlaywrightIssueToken.php` |
|
||
| 모듈 권한 (`sirsoft-ecommerce.*`) | 모듈 (코어 커맨드의 `--permissions=` 임의 식별자) | — |
|
||
| 모듈 도메인 데이터 (상품/카테고리/주문) | 모듈 | `modules/_bundled/{id}/src/Console/Commands/PlaywrightSeed{id}.php` |
|
||
| 플러그인 도메인 데이터 (결제 키 등) | 플러그인 | `plugins/_bundled/{id}/src/Console/Commands/PlaywrightSeed{id}.php` |
|
||
| 외부 의존 (토스 결제창 응답 등) | spec 안 mock | `page.route('https://api.tosspayments.com/**', ...)` |
|
||
|
||
**핵심 원칙**: 코어는 모듈 도메인을 모른다. 모듈 도메인 시드 커맨드를 코어에 두면 코어↔모듈 의존이 뒤집힘 (정적 검사 위반).
|
||
|
||
## 시나리오 매니페스트 ↔ spec 매핑
|
||
|
||
시나리오 매니페스트(`tests/scenarios/*.yaml`)의 축과 spec 은 파일명으로 대응시킨다 —
|
||
`layout-editor-table-editor.yaml` ↔ `specs/layout-editor/table-editor.spec.ts` 처럼
|
||
매니페스트 이름에서 도메인 접두어를 뗀 형태가 spec 파일명이 된다.
|
||
|
||
각 spec 은 `test.describe.parallel(axisName)` 으로 묶고 matrix 배열로 매개변수화한다.
|
||
케이스 docblock 에 `// @scenario k1=v1, k2=v2` / `// @effects e1, e2` 를 마킹하면
|
||
매니페스트의 `cross_product` · `effects` 와 자동으로 대조된다.
|
||
|
||
`@scenario` 의 축 구분자는 **쉼표뿐이다.** `k1=v1 × k2=v2` 처럼 곱셈 기호로 적으면
|
||
첫 축만 인식되고 나머지는 조용히 누락된다.
|
||
|
||
## 외부 의존 시나리오 — mock-first 패턴 (결제 등)
|
||
|
||
토스페이먼츠 등 외부 도메인 결제 흐름은 결정론적 자동화에 **mock-first 전략** 필수.
|
||
|
||
```typescript
|
||
// 토스 SDK 호출 가로채기 — 결제창 띄우지 않고 즉시 success callback URL 로 redirect
|
||
await page.route('https://js.tosspayments.com/**', route => route.fulfill({
|
||
contentType: 'application/javascript',
|
||
body: `
|
||
window.TossPayments = () => ({
|
||
requestPayment: async (method, params) => {
|
||
window.location.href = params.successUrl
|
||
+ '?paymentKey=test_pk_xxx&orderId=' + params.orderId + '&amount=' + params.amount;
|
||
}
|
||
});
|
||
`,
|
||
}));
|
||
|
||
// 토스 confirm API mock — 가맹점 측 callback 흐름 검증
|
||
await page.route('https://api.tosspayments.com/v1/payments/confirm', route => route.fulfill({
|
||
status: 200, contentType: 'application/json',
|
||
body: JSON.stringify({
|
||
paymentKey: 'test_pk_xxx', orderId: 'order_001', status: 'DONE',
|
||
method: '카드', totalAmount: 10000, approvedAt: new Date().toISOString(),
|
||
}),
|
||
}));
|
||
```
|
||
|
||
분기별(성공/실패/취소/금액 불일치/중복) 가맹점 측 처리(주문 상태 전환, 재고 차감, 알림 발송) 정확성 검증.
|
||
실 카드 결제 자동화는 권장하지 않음 — 수동 검증.
|
||
|
||
## 회귀 / Flaky 처리
|
||
|
||
- `retries: process.env.CI ? 2 : 0` — CI 에서만 자동 retry
|
||
- `trace: 'retain-on-failure'` — 실패 시 trace.zip 보존 (`npx playwright show-trace ...`)
|
||
- 2주 내 2회 이상 fail = `@flaky` 태그 격리 + RCA
|
||
|
||
## 디버깅 워크플로우
|
||
|
||
```powershell
|
||
# UI 모드 — 단계별 step 시각화
|
||
$env:PLAYWRIGHT_BASE_URL='https://g7.dev'; npm run test:e2e:ui
|
||
|
||
# 특정 spec 만 실행
|
||
PLAYWRIGHT_BASE_URL=https://g7.dev npx playwright test tests/Playwright/specs/layout-editor/auth-guard-and-locales.spec.ts
|
||
|
||
# trace 열기
|
||
npx playwright show-trace test-results/<...>/trace.zip
|
||
```
|
||
|
||
Playwright 가 회귀를 발견하면 → Claude `chrome-devtools-mcp` 로 라이브 재현/진단 → spec 으로 승격.
|
||
|
||
## 참고
|
||
|
||
- 가이드: `docs/testing/e2e-testing.md`
|
||
- 시나리오 매니페스트: `tests/scenarios/` (레이아웃 편집기 축은 `layout-editor-*.yaml`)
|
||
- 단위 시뮬레이션 (Vitest): `resources/js/core/template-engine/__tests__/`
|
||
- 모듈 sample skeleton: `modules/_bundled/sirsoft-ecommerce/tests/Playwright/README.md`
|