Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: CI

# PR과 main 푸시에서 lint·타입체크·단위·E2E를 검증한다.
on:
push:
branches: [main]
pull_request:
branches: [main]

# 같은 브랜치/PR의 진행 중 실행은 취소해 러너 사용량을 줄인다.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: lint · type-check · unit · e2e
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

# pnpm을 먼저 설치해야 setup-node의 pnpm 캐시가 동작한다.
# 버전은 루트 package.json의 packageManager(pnpm@9.0.0) 필드를 따른다.
- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Lint
run: pnpm --filter blog lint

- name: Type check
run: pnpm --filter blog exec tsc --noEmit

- name: Unit tests (Jest)
run: pnpm --filter blog test

# E2E용 Chromium과 OS 의존성을 설치한다.
- name: Install Playwright browser
run: pnpm --filter blog exec playwright install --with-deps chromium

# CI=true가 자동 주입되므로 playwright.config가 프로덕션 빌드(build && start)로 검증한다.
- name: E2E tests (Playwright)
run: pnpm --filter blog test:e2e

# 실패 시 분석용 HTML 리포트(트레이스 포함)를 아티팩트로 올린다.
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: apps/blog/playwright-report/
retention-days: 7
if-no-files-found: ignore
10 changes: 5 additions & 5 deletions apps/blog/e2e/responsive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ test.describe('모바일 반응형', () => {
// 데스크톱 전용 Tags 네비 링크는 md 미만에서 숨겨져야 한다.
await expect(page.getByRole('link', { name: 'Tags' })).toBeHidden();

// 햄버거(메뉴) 아이콘을 눌러 사이드바를 연다.
await page.locator('.tabler-icon-menu-2').click();
// 햄버거(메뉴) 버튼을 눌러 사이드바를 연다(#87에서 추가된 접근성 라벨 사용).
await page.getByRole('button', { name: '메뉴 열기' }).click();

// 닫혀 있을 땐 화면 밖(translate-x-full)에 있던 사이드바의 Series 섹션 단락이
// 닫혀 있을 땐 화면 밖(translate-x-full)에 있던 사이드바 패널(dialog)이
// 열린 뒤에는 뷰포트 안으로 들어와야 한다(패널이 실제로 슬라이드인됐음을 검증).
const sidebarSeries = page.getByRole('paragraph').filter({ hasText: /^Series$/ });
await expect(sidebarSeries).toBeInViewport();
const sidebar = page.getByRole('dialog', { name: '사이트 메뉴' });
await expect(sidebar).toBeInViewport();
});
});
4 changes: 2 additions & 2 deletions apps/blog/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export default defineConfig({
timeout: 60_000,
// CI에서만 실패 1회 재시도로 일시적 플레이키를 흡수한다(로컬은 즉시 실패가 디버깅에 유리).
retries: process.env.CI ? 1 : 0,
// 실패 원인 추적용 리포터. 로컬에서 결과를 바로 열어볼 수 있게 한다.
reporter: process.env.CI ? 'github' : 'list',
// 리포터: 로컬은 간결한 list, CI는 GitHub 어노테이션 + 실패 분석용 HTML 리포트(아티팩트 업로드용).
reporter: process.env.CI ? [['github'], ['html', { open: 'never' }]] : 'list',
use: {
// 모든 테스트가 동일 출처를 바라보도록 기준 URL을 둔다(상대경로 goto 가능).
baseURL: 'http://localhost:3000',
Expand Down
Loading