test(frontend): #343 Jest+RTL 인프라 + ARIA Tabs + remotePatterns

테스트 인프라:
- Jest 30 + jest-environment-jsdom + RTL + jest-dom matchers
- next/jest로 SWC/Next.js 자동 통합
- jest.config.ts (setupFilesAfterEnv) + jest.setup.ts
- npm scripts: test, test:watch
- 샘플 테스트 3개, 13/13 통과:
  - i18n/config: isLocale + detectBrowserLocale (5 케이스)
  - Stars 컴포넌트: 별점/aria/clamp/showNumber (5 케이스)
  - admin-utils: getAdminToken + authHeaders (4 케이스)

ARIA Tabs (MyReviewsList):
- role=tablist + tab + aria-selected + aria-controls + tabIndex
- panel에 role=tabpanel + aria-labelledby

next/image:
- next.config.ts remotePatterns: lh3.googleusercontent.com / i.ytimg.com / yt3.ggpht.com
- ReviewSection의 user_avatar_url에 명시적 eslint-disable + 사유

후속(별도): 전체 컴포넌트 테스트 점진 추가, 백엔드 JUnit 인프라, E2E (Playwright), CI 통합

설계서: docs/design/343-frontend-test-infra/README.md

Refs: #343
This commit is contained in:
joungmin
2026-06-15 16:25:55 +09:00
parent 9ba905aad8
commit 730727a7a6
10 changed files with 4493 additions and 34 deletions

View File

@@ -41,8 +41,14 @@ export default function MyReviewsList({
</button>
</div>
<div className="flex gap-1 border-b">
{/* #343 — WAI-ARIA Tabs 패턴 */}
<div role="tablist" aria-label="내 활동" className="flex gap-1 border-b">
<button
role="tab"
id="tab-reviews"
aria-selected={tab === "reviews"}
aria-controls="panel-reviews"
tabIndex={tab === "reviews" ? 0 : -1}
onClick={() => setTab("reviews")}
className={`px-3 py-1.5 text-sm font-medium border-b-2 transition-colors ${
tab === "reviews"
@@ -54,6 +60,11 @@ export default function MyReviewsList({
({reviews.length})
</button>
<button
role="tab"
id="tab-memos"
aria-selected={tab === "memos"}
aria-controls="panel-memos"
tabIndex={tab === "memos" ? 0 : -1}
onClick={() => setTab("memos")}
className={`px-3 py-1.5 text-sm font-medium border-b-2 transition-colors ${
tab === "memos"
@@ -67,7 +78,8 @@ export default function MyReviewsList({
</div>
{tab === "reviews" ? (
reviews.length === 0 ? (
<div role="tabpanel" id="panel-reviews" aria-labelledby="tab-reviews">
{reviews.length === 0 ? (
<p className="text-sm text-gray-500 py-8 text-center">
.
</p>
@@ -100,9 +112,11 @@ export default function MyReviewsList({
</button>
))}
</div>
)
)}
</div>
) : (
memos.length === 0 ? (
<div role="tabpanel" id="panel-memos" aria-labelledby="tab-memos">
{memos.length === 0 ? (
<p className="text-sm text-gray-500 py-8 text-center">
.
</p>
@@ -137,7 +151,8 @@ export default function MyReviewsList({
</button>
))}
</div>
)
)}
</div>
)}
</div>
);