Skip to content

Commit af085b1

Browse files
committed
test: 테스트 커버리지 작업
1 parent a500f7a commit af085b1

File tree

8 files changed

+329
-27
lines changed

8 files changed

+329
-27
lines changed

src/features/post/information/ProfileSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Link from 'next/link';
22
import type { FunctionComponent } from 'react';
33
import { MyProfile } from '~/about/headline/data/profile';
4-
import { Avatar } from '~/shared/components/Avatar';
4+
import { Avatar } from '~/shared/components/Avatar/Container';
55

66
export const ProfileSection: FunctionComponent = () => {
77
return (

src/features/shared/components/Avatar.spec.tsx

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { render, screen } from '@testing-library/react';
2+
import { Avatar } from './Container';
3+
import { MyProfile } from '~/about/headline/data/profile';
4+
5+
describe('rendering', () => {
6+
it('should visible', async () => {
7+
render(<Avatar />);
8+
expect(
9+
screen.getByRole('img', { name: '1ilsang character' }),
10+
).toBeVisible();
11+
});
12+
13+
it('should visible with nav prop', async () => {
14+
render(<Avatar nav />);
15+
expect(
16+
screen.getByRole('img', { name: '1ilsang character' }),
17+
).toBeVisible();
18+
});
19+
});
20+
21+
describe('attr', () => {
22+
it('should href be "/about"', async () => {
23+
render(<Avatar />);
24+
expect(screen.getByRole('link')).toHaveAttribute('href', '/about');
25+
});
26+
27+
it('should same MyProfile', async () => {
28+
render(<Avatar />);
29+
const img = screen.getByAltText(MyProfile.personal.alt);
30+
expect(img).toHaveAttribute('src', MyProfile.personal.imageSrc);
31+
});
32+
});
33+
34+
describe('styles', () => {
35+
it('should have default styles when nav is false', async () => {
36+
render(<Avatar />);
37+
const container = screen.getByRole('img', { name: '1ilsang character' });
38+
expect(container).toHaveClass('mr-2', 'w-9', 'h-9', 'md:w-12', 'md:h-12');
39+
40+
const img = screen.getByAltText(MyProfile.personal.alt);
41+
expect(img).toHaveClass(
42+
'border',
43+
'border-solid',
44+
'rounded-full',
45+
'border-sub-blue',
46+
);
47+
});
48+
49+
it('should have nav styles when nav is true', async () => {
50+
render(<Avatar nav />);
51+
const container = screen.getByRole('img', { name: '1ilsang character' });
52+
expect(container).toHaveClass('mr-2', 'w-8', 'h-8', 'mt-2');
53+
54+
const img = screen.getByAltText(MyProfile.personal.alt);
55+
expect(img).toHaveClass(
56+
'border',
57+
'border-solid',
58+
'rounded-full',
59+
'border-white-blue',
60+
);
61+
});
62+
});
63+
64+
describe('accessibility', () => {
65+
it('should have proper ARIA attributes', async () => {
66+
render(<Avatar />);
67+
const container = screen.getByRole('img', { name: '1ilsang character' });
68+
expect(container).toHaveAttribute('aria-label', '1ilsang character');
69+
});
70+
71+
it('should have proper image alt text', async () => {
72+
render(<Avatar />);
73+
const img = screen.getByAltText(MyProfile.personal.alt);
74+
expect(img).toBeInTheDocument();
75+
});
76+
});
77+
78+
describe('component', () => {
79+
it('should have proper displayName', () => {
80+
expect(Avatar.displayName).toBe('Avatar');
81+
});
82+
});
File renamed without changes.

src/features/shared/components/ExternalLink.spec.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { render, screen } from '@testing-library/react';
2+
import userEvent from '@testing-library/user-event';
23
import ExternalLink, { highlighterClass } from './ExternalLink';
4+
import { ga } from '../helpers/logger';
5+
6+
jest.mock('../helpers/logger', () => ({
7+
ga: jest.fn(),
8+
}));
39

410
describe('rendering', () => {
511
it('should visible', async () => {
@@ -37,6 +43,23 @@ describe('rendering', () => {
3743
...[highlighterClass, propClass],
3844
);
3945
});
46+
47+
it('should disable default CSS transition when prop is true', async () => {
48+
render(<ExternalLink href="1ilsang.dev" disableDefaultCSSTransition />);
49+
expect(screen.getByRole('link')).not.toHaveClass(highlighterClass);
50+
});
51+
52+
it('should render with both label and children', async () => {
53+
const label = 'My Link';
54+
const childText = ' - Click here';
55+
render(
56+
<ExternalLink href="1ilsang.dev" label={label}>
57+
{childText}
58+
</ExternalLink>,
59+
);
60+
const link = screen.getByRole('link');
61+
expect(link).toHaveTextContent(`${label}${childText}`);
62+
});
4063
});
4164

4265
describe('attr', () => {
@@ -59,3 +82,34 @@ describe('attr', () => {
5982
);
6083
});
6184
});
85+
86+
describe('interactions', () => {
87+
beforeEach(() => {
88+
jest.clearAllMocks();
89+
});
90+
91+
it('should call ga tracking when clicked with href as value', async () => {
92+
const href = 'https://example.com';
93+
render(<ExternalLink href={href} />);
94+
95+
await userEvent.click(screen.getByRole('link'));
96+
97+
expect(ga).toHaveBeenCalledWith('linkClick', {
98+
type: 'external-link',
99+
value: href,
100+
});
101+
});
102+
103+
it('should call ga tracking when clicked with label as value', async () => {
104+
const href = 'https://example.com';
105+
const label = 'Example Site';
106+
render(<ExternalLink href={href} label={label} />);
107+
108+
await userEvent.click(screen.getByRole('link'));
109+
110+
expect(ga).toHaveBeenCalledWith('linkClick', {
111+
type: 'external-link',
112+
value: label,
113+
});
114+
});
115+
});

src/features/shared/components/Typography/Typography.spec.tsx

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,39 @@ describe('Typography Component', () => {
160160
expect(screen.getByRole('paragraph')).toBeInTheDocument();
161161
});
162162

163+
it('isValidElement가 false인 경우를 처리해야 한다', () => {
164+
const invalidElement = 'string content';
165+
render(<Typography>{invalidElement}</Typography>);
166+
167+
expect(screen.getByText('string content')).toBeInTheDocument();
168+
});
169+
170+
it('children이 없는 요소를 처리해야 한다', () => {
171+
const ElementWithoutChildren = ({ ...props }) => <span {...props} />;
172+
render(
173+
<Typography>
174+
<ElementWithoutChildren />
175+
</Typography>,
176+
);
177+
178+
// 에러 없이 렌더링되어야 함
179+
const paragraph = screen.getByRole('paragraph');
180+
expect(paragraph).toBeInTheDocument();
181+
});
182+
183+
it('props가 undefined인 요소를 처리해야 한다', () => {
184+
// processChild 함수를 직접 테스트할 수 없으므로,
185+
// 실제 시나리오와 가까운 테스트를 작성
186+
render(
187+
<Typography>
188+
<span />
189+
</Typography>,
190+
);
191+
192+
const paragraph = screen.getByRole('paragraph');
193+
expect(paragraph).toBeInTheDocument();
194+
});
195+
163196
it('매우 깊은 중첩 구조에서 최대 깊이 제한을 적용해야 한다', () => {
164197
// 15단계 깊이의 중첩 구조 생성 (최대 10단계로 제한됨)
165198
let nestedElement = <span>깊은 텍스트</span>;
@@ -213,5 +246,39 @@ describe('Typography Component', () => {
213246
expect(pElement?.textContent).toContain('일반 텍스트');
214247
expect(pElement?.textContent).toContain('더 많은 텍스트');
215248
});
249+
250+
it('children이 배열인 경우를 처리해야 한다', () => {
251+
const arrayChildren = ['첫 번째', '두 번째'];
252+
253+
render(
254+
<Typography>
255+
<span>{arrayChildren}</span>
256+
</Typography>,
257+
);
258+
259+
expect(screen.getByText('첫 번째두 번째')).toBeInTheDocument();
260+
});
261+
262+
it('복잡한 React 요소 배열을 처리해야 한다', () => {
263+
const complexChildren = [
264+
<span key="1">첫 번째 span</span>,
265+
'텍스트',
266+
<span key="2">두 번째 span</span>,
267+
];
268+
269+
render(
270+
<Typography>
271+
<span>{complexChildren}</span>
272+
</Typography>,
273+
);
274+
275+
// 각각의 텍스트가 존재하는지 확인
276+
expect(screen.getByText(/ span/)).toBeInTheDocument();
277+
expect(screen.getByText(/ span/)).toBeInTheDocument();
278+
});
279+
280+
it('displayName이 올바르게 설정되어 있어야 한다', () => {
281+
expect(Typography.displayName).toBe('Typography');
282+
});
216283
});
217284
});

src/features/shared/components/nav/Navbar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { usePathname } from 'next/navigation';
55
import type { FunctionComponent } from 'react';
66
import { memo, useEffect, useMemo, useState } from 'react';
77
import classNames from 'classnames';
8-
import { Avatar } from '../Avatar';
8+
import { Avatar } from '../Avatar/Container';
99
import { usePrint } from '~/shared/hooks/usePrint';
1010

1111
interface NavTextProps {

0 commit comments

Comments
 (0)