Files
Gnuboard7/tests/Playwright
HeuJung 68cbee0e83 fix(engine): 좁은 창에서 레이아웃 편집기가 찌그러지는 문제
레이아웃 편집기는 라우트 트리(280px 고정) + 디바이스 미리보기 캔버스 +
라벨 있는 툴바 버튼 10여 개가 나란히 놓이는 대화면 전용 도구다. 그런데
셸에 너비 하한이 없어 창을 좁히면 이들이 창 너비에 맞춰 압착됐고, 툴바가
flex row 라 각 항목 폭이 깎이면서 라벨이 글자 단위로 줄바꿈됐다("요 소 추 가").

반응형으로 재배치하는 대신 셸이 최소 너비(EDITOR_MIN_WIDTH=1280)를 유지하고
모자란 폭은 브라우저 가로 스크롤이 흡수하도록 했다. 편집기 UI 는 축약 대상이
아니며, 압착을 허용하면 캔버스 실측 폭이 뷰포트에 종속돼 디바이스 미리보기의
의미 자체가 사라진다.

툴바 압착 차단은 직접 자식 전체를 덮는 CSS 규칙으로 뒀다. 버튼마다 인라인
style 로 붙이면 자체 style 을 가진 하위 컴포넌트(템플릿 전환기·언어 전환기)와
이후 추가될 항목이 빠져 같은 결함이 재발한다.

jsdom 은 레이아웃 엔진이 없어 단위 테스트로는 "style 선언 존재" 까지만 잠긴다.
실제로 압착이 멈췄는지는 렌더 박스를 재는 브라우저에서만 증명되므로 Playwright
spec 을 함께 추가했다(수정 되돌림 시 red 확인). 아울러 이 파일의 e2e:allow 면제
주석은 사유("브라우저 가시 UI 무변경")가 더 이상 참이 아니라 제거했다.
2026-07-12 16:13:51 +09:00
..

Playwright E2E (코어 영역)

G7 코어의 Playwright (TypeScript) 기반 결정론적 E2E 인프라.

빠른 시작

# PowerShell (개발 환경)
$env:PLAYWRIGHT_BASE_URL='https://g7.dev'
npm run test:e2e            # 전체 spec
npm run test:e2e:smoke      # @smoke 태그 (homepage / login / admin-dashboard 3건)
npm run test:e2e:wysiwyg    # 위지윅 매트릭스 90건 (Phase M)
npm run test:e2e:ui         # Playwright UI 모드 (디버깅)
# Bash (CI/Linux)
PLAYWRIGHT_BASE_URL=https://g7.dev npx playwright test

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 하드코딩 금지.

디렉토리 구조

tests/Playwright/
├── fixtures/
│   └── auth.ts                        (Sanctum 토큰 발급 + addInitScript 주입)
├── specs/
│   ├── smoke/                         (Phase 1 — 3건)
│   │   ├── homepage.spec.ts
│   │   ├── login.spec.ts
│   │   └── admin-dashboard.spec.ts
│   └── wysiwyg/                       (Phase 2 — 90건, 위지윅 편집기 Phase M)
│       ├── auth-guard.spec.ts                (12: access_outcome × user_permission)
│       ├── handler-suppression.spec.ts       (6:  suppressed_handler 축)
│       ├── anchor-intercept.spec.ts          (45: anchor_kind × modifier_key)
│       └── url-template-param.spec.ts        (27: url_template_param × anchor_kind)
├── .auth/                             (gitignore — storageState 캐시)
└── README.md                          (본 문서)

코어/확장 분리 원칙

기존 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 작성자는 가드를 인지할 필요 없음.

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/**', ...)

핵심 원칙: 코어는 모듈 도메인을 모른다. 모듈 도메인 시드 커맨드를 코어에 두면 코어↔모듈 의존이 뒤집힘 (audit 룰 위반).

시나리오 매니페스트 ↔ spec 매핑

YAML axis spec 파일 케이스
tests/scenarios/wysiwyg-editor-access-guard.yaml cross_product[0] (access_outcome × user_permission) specs/wysiwyg/auth-guard.spec.ts 12
동일 yaml cross_product[1] (anchor_kind × modifier_key) specs/wysiwyg/anchor-intercept.spec.ts 45
동일 yaml cross_product[2] (suppressed_handler) specs/wysiwyg/handler-suppression.spec.ts 6
동일 yaml cross_product[3] (url_template_param × anchor_kind) specs/wysiwyg/url-template-param.spec.ts 27

각 spec 은 test.describe.parallel(axisName) 으로 묶고 matrix 배열로 매개변수화. 케이스 docblock 에 // @scenario k1=v1, k2=v2 / // @effects e1, e2 마킹.

외부 의존 시나리오 — mock-first 패턴 (결제 등)

토스페이먼츠 등 외부 도메인 결제 흐름은 결정론적 자동화에 mock-first 전략 필수.

// 토스 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

디버깅 워크플로우

# 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/wysiwyg/auth-guard.spec.ts

# trace 열기
npx playwright show-trace test-results/<...>/trace.zip

Playwright 가 회귀를 발견하면 → Claude chrome-devtools-mcp 로 라이브 재현/진단 → spec 으로 승격.

참고

  • 가이드: docs/testing/e2e-testing.md
  • 시나리오 매니페스트: tests/scenarios/wysiwyg-editor-access-guard.yaml
  • 단위 시뮬레이션 (Vitest): resources/js/core/template-engine/wysiwyg/__tests__/previewNavigation.test.ts 등
  • 모듈 sample skeleton: modules/_bundled/sirsoft-ecommerce/tests/Playwright/README.md