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:
36
frontend/__tests__/Stars.test.tsx
Normal file
36
frontend/__tests__/Stars.test.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* #343 — Stars 컴포넌트 렌더 테스트.
|
||||
*/
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import Stars from "@/components/Stars";
|
||||
|
||||
describe("Stars", () => {
|
||||
it("renders 5 star slots", () => {
|
||||
const { container } = render(<Stars rating={3} />);
|
||||
// 빈 별 5개 (text-gray-300 클래스 갖는 span)
|
||||
const emptyStars = container.querySelectorAll("span.text-gray-300");
|
||||
expect(emptyStars.length).toBe(5);
|
||||
});
|
||||
|
||||
it("shows aria-label with rating", () => {
|
||||
render(<Stars rating={4.5} />);
|
||||
expect(screen.getByLabelText("4.5점")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("clamps rating to 0~5", () => {
|
||||
render(<Stars rating={-1} />);
|
||||
expect(screen.getByLabelText("0점")).toBeInTheDocument();
|
||||
render(<Stars rating={10} />);
|
||||
expect(screen.getByLabelText("5점")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows number when showNumber + rating > 0", () => {
|
||||
const { container } = render(<Stars rating={3.5} showNumber />);
|
||||
expect(container.textContent).toContain("3.5");
|
||||
});
|
||||
|
||||
it("does not show number when rating is 0 even with showNumber", () => {
|
||||
const { container } = render(<Stars rating={0} showNumber />);
|
||||
expect(container.textContent).not.toContain("0");
|
||||
});
|
||||
});
|
||||
28
frontend/__tests__/admin-utils.test.ts
Normal file
28
frontend/__tests__/admin-utils.test.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* #343 — admin-utils 순수 함수 단위 테스트.
|
||||
*/
|
||||
import { getAdminToken, authHeaders } from "@/lib/admin-utils";
|
||||
|
||||
describe("admin-utils", () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
it("getAdminToken returns null when not set", () => {
|
||||
expect(getAdminToken()).toBeNull();
|
||||
});
|
||||
|
||||
it("getAdminToken returns stored token", () => {
|
||||
localStorage.setItem("tasteby_token", "abc123");
|
||||
expect(getAdminToken()).toBe("abc123");
|
||||
});
|
||||
|
||||
it("authHeaders is empty when no token", () => {
|
||||
expect(authHeaders()).toEqual({});
|
||||
});
|
||||
|
||||
it("authHeaders includes Bearer when token set", () => {
|
||||
localStorage.setItem("tasteby_token", "xyz");
|
||||
expect(authHeaders()).toEqual({ Authorization: "Bearer xyz" });
|
||||
});
|
||||
});
|
||||
42
frontend/__tests__/i18n-config.test.ts
Normal file
42
frontend/__tests__/i18n-config.test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* #343 — i18n/config 순수 함수 단위 테스트.
|
||||
*/
|
||||
import { isLocale, detectBrowserLocale, DEFAULT_LOCALE } from "@/i18n/config";
|
||||
|
||||
describe("i18n/config.isLocale", () => {
|
||||
it("returns true for supported locales", () => {
|
||||
expect(isLocale("ko")).toBe(true);
|
||||
expect(isLocale("en")).toBe(true);
|
||||
expect(isLocale("ja")).toBe(true);
|
||||
expect(isLocale("es")).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for unsupported / null / undefined", () => {
|
||||
expect(isLocale("fr")).toBe(false);
|
||||
expect(isLocale("zh")).toBe(false);
|
||||
expect(isLocale(null)).toBe(false);
|
||||
expect(isLocale(undefined)).toBe(false);
|
||||
expect(isLocale("")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("i18n/config.detectBrowserLocale", () => {
|
||||
// jsdom의 navigator.language는 기본 'en-US'
|
||||
it("returns supported locale from navigator.language", () => {
|
||||
Object.defineProperty(navigator, "language", { value: "en-US", configurable: true });
|
||||
expect(detectBrowserLocale()).toBe("en");
|
||||
Object.defineProperty(navigator, "language", { value: "ko-KR", configurable: true });
|
||||
expect(detectBrowserLocale()).toBe("ko");
|
||||
Object.defineProperty(navigator, "language", { value: "ja", configurable: true });
|
||||
expect(detectBrowserLocale()).toBe("ja");
|
||||
Object.defineProperty(navigator, "language", { value: "es-MX", configurable: true });
|
||||
expect(detectBrowserLocale()).toBe("es");
|
||||
});
|
||||
|
||||
it("falls back to DEFAULT_LOCALE for unsupported", () => {
|
||||
Object.defineProperty(navigator, "language", { value: "fr-FR", configurable: true });
|
||||
expect(detectBrowserLocale()).toBe(DEFAULT_LOCALE);
|
||||
Object.defineProperty(navigator, "language", { value: "zh-CN", configurable: true });
|
||||
expect(detectBrowserLocale()).toBe(DEFAULT_LOCALE);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user