Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 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
8 changes: 0 additions & 8 deletions src/components/global/Footer/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,8 @@
.group {
width: 100%;
height: 113px;
display: flex;
flex-direction: column-reverse;
align-items: center;
justify-content: center;
gap: 16px;

@include desktop {
height: 100px;
flex-direction: row;
justify-content: space-between;
gap: 0;
}
}
42 changes: 42 additions & 0 deletions src/components/global/Footer/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { ReactNode } from 'react';

import { render, screen } from '@testing-library/react';

import Footer from './index';

vi.mock('@/components/atoms/Layout', () => ({
default: ({ children }: { children: ReactNode }) => (
<div data-testid="layout">{children}</div>
),
}));

vi.mock('@/components/molecules/SocialIconLink', () => ({
default: ({ type, url }: { type: string; url: string }) => (
<a href={url}>{type}</a>
),
}));

describe('Footer', () => {
it('renders the footer copyright and social links', () => {
render(<Footer />);

expect(screen.getByRole('contentinfo')).toBeInTheDocument();
expect(screen.getByText('All rights reserved ⓒ SIPE')).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'INSTAGRAM' })).toHaveAttribute(
'href',
'https://www.instagram.com/sipe_team',
);
expect(screen.getByRole('link', { name: 'GITHUB' })).toHaveAttribute(
'href',
'https://github.com/sipe-team',
);
expect(screen.getByRole('link', { name: 'YOUTUBE' })).toHaveAttribute(
'href',
'https://www.youtube.com/@sipe_team',
);
expect(screen.getByRole('link', { name: 'LINKEDIN' })).toHaveAttribute(
'href',
'https://www.linkedin.com/company/sipe.team',
);
});
});
10 changes: 8 additions & 2 deletions src/components/global/Footer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ function Footer() {
<Flex asChild className={styles.wrapper} direction="row" justify="center">
<footer>
<Layout>
<div className={styles.group}>
<Flex
align="center"
className={styles.group}
direction={{ sm: 'column-reverse', lg: 'row' }}
gap={{ sm: '16px', lg: 0 }}
justify={{ sm: 'center', lg: 'space-between' }}
>
<Typography color={color.white} size={12} weight="medium">
All rights reserved ⓒ SIPE
</Typography>
Expand All @@ -32,7 +38,7 @@ function Footer() {
url="https://www.linkedin.com/company/sipe.team"
/>
</Flex>
</div>
</Flex>
</Layout>
</footer>
</Flex>
Expand Down
5 changes: 0 additions & 5 deletions src/components/global/Navigation/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@

.menuList {
width: 100%;
flex-direction: column;
align-items: center;
padding-bottom: 20px;

@include mobile-and-tablet {
Expand All @@ -60,9 +58,6 @@
}

@include desktop {
flex-direction: row;
justify-content: center;
align-items: center;
padding: 0;
}
}
Expand Down
141 changes: 141 additions & 0 deletions src/components/global/Navigation/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import type { ReactNode } from 'react';

import { fireEvent, render, screen } from '@testing-library/react';

import Navigation from './index';

const { sendGAEvent } = vi.hoisted(() => ({
sendGAEvent: vi.fn(),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분 아래에 있는,

RecruitmentStatusSection/index.test.tsx 에서,

vi.mock('@next/third-parties/google', () => ({
  sendGAEvent: vi.fn(),
}));
vi.mocked(sendGAEvent).mockClear();

쓰는 것처럼 통일해서 쓰는 건 어떨까요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀 주신 방식으로 통일하겠습니다. @next/third-parties/google에서 sendGAEvent를 import하고, mock에는 vi.fn()을 직접 선언한 뒤 vi.mocked(sendGAEvent).mockClear()로 초기화하도록 수정하겠습니다.

}));

vi.mock('next/link', () => ({
default: ({ children, href }: { children: ReactNode; href: string }) => (
<a href={href}>{children}</a>
),
}));

vi.mock('next/navigation', () => ({
usePathname: () => '/about',
}));

vi.mock('@next/third-parties/google', () => ({
sendGAEvent,
}));

vi.mock('@/components/atoms/Layout', () => ({
default: ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => <div className={className}>{children}</div>,
}));

vi.mock('@/components/global/HamburgerButton', () => ({
default: ({
isOpened,
onClick,
}: {
isOpened: boolean;
onClick: () => void;
}) => (
<button aria-expanded={isOpened} aria-label="메뉴" onClick={onClick}>
메뉴
</button>
),
}));

vi.mock('@/components/molecules/Button', () => ({
default: ({
active,
children,
disabled,
href,
onClick,
}: {
active?: boolean;
children: ReactNode;
disabled?: boolean;
href?: string;
onClick?: () => void;
}) => (
<a
aria-current={active ? 'page' : undefined}
aria-disabled={disabled}
href={href}
onClick={(event) => {
event.preventDefault();
onClick?.();
}}
>
{children}
</a>
),
}));

vi.mock('@/libs/assets/logos', () => ({
SipeLogo: () => <svg aria-label="사이프 로고" />,
}));

vi.mock('@/libs/utils/recruit', () => ({
displayApplication: {
ongoing: {
buttonText: '지원하기',
formUrl: 'https://example.com/apply',
},
},
getCurrentStatus: () => 'ongoing',
}));

describe('Navigation', () => {
beforeEach(() => {
sendGAEvent.mockClear();
});

it('renders the current menu, navigation links, and application link', () => {
render(<Navigation />);

expect(screen.getByRole('banner')).toBeInTheDocument();
expect(screen.getByRole('navigation')).toBeInTheDocument();
expect(screen.getByLabelText('사이프 로고').closest('a')).toHaveAttribute(
'href',
'/',
);
expect(screen.getByRole('link', { name: 'About' })).toHaveAttribute(
'aria-current',
'page',
);
expect(screen.getByRole('link', { name: 'Recruit' })).toHaveAttribute(
'href',
'/recruit',
);
expect(screen.getByRole('link', { name: 'People' })).toHaveAttribute(
'href',
'/people',
);
expect(screen.getByRole('link', { name: 'Activity' })).toHaveAttribute(
'href',
'/activity',
);
expect(screen.getByRole('link', { name: 'Join Us' })).toHaveAttribute(
'href',
'https://example.com/apply',
);
});

it('toggles the mobile menu and reports application clicks', () => {
render(<Navigation />);

const menuButton = screen.getByRole('button', { name: '메뉴' });
expect(menuButton).toHaveAttribute('aria-expanded', 'false');

fireEvent.click(menuButton);
expect(menuButton).toHaveAttribute('aria-expanded', 'true');

fireEvent.click(screen.getByRole('link', { name: 'Join Us' }));
expect(sendGAEvent).toHaveBeenCalledWith('event', 'cilck_join_us_button', {
screen_name: 'menu',
});
});
});
3 changes: 3 additions & 0 deletions src/components/global/Navigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,14 @@ function Navigation() {
>
<nav>
<Flex
align="center"
className={clsx(
styles.menuList,
isMobileMenuOpen ? styles.open : styles.close,
)}
direction={{ sm: 'column', lg: 'row' }}
gap="12px"
justify={{ lg: 'center' }}
>
{menus.map((menu) => (
<Button
Expand Down
5 changes: 0 additions & 5 deletions src/components/molecules/Card/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@
}

@include desktop {
&.reverse {
flex-direction: row-reverse;
}

flex-direction: row;
margin-bottom: 120px;
}
}
Expand Down
5 changes: 2 additions & 3 deletions src/components/molecules/Card/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Flex } from '@sipe-team/side';
import clsx from 'clsx';

import Badge from '@/components/atoms/Badge';
import Image from '@/components/molecules/Image';
Expand All @@ -18,8 +17,8 @@ function Card({ src, badgeText, title, subTitle, reverse }: CardProps) {
return (
<Flex
align="flex-start"
className={clsx(styles.section, reverse && styles.reverse)}
direction="column"
className={styles.section}
direction={{ sm: 'column', lg: reverse ? 'row-reverse' : 'row' }}
gap="24px"
grow={1}
justify="flex-start"
Expand Down
6 changes: 0 additions & 6 deletions src/components/molecules/Table/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
padding: 0;

@include mobile {
gap: 16px;

> * {
width: 100%;
}
Expand All @@ -26,16 +24,12 @@

@include mobile {
height: auto;
flex-direction: row;
align-items: center;
}
}

@include mobile {
width: 100%;
height: auto;
align-items: start;
flex-direction: column;
}
}

Expand Down
66 changes: 66 additions & 0 deletions src/components/molecules/Table/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { render, screen } from '@testing-library/react';

import Table from './index';

vi.mock('@/components/atoms/Badge', () => ({
default: ({ text }: { text: string }) => <span>{text}</span>,
}));

vi.mock('@/components/molecules/GlowArea', () => ({
default: ({ children }: { children: React.ReactNode }) => (
<div data-testid="glow-area">{children}</div>
),
}));

vi.mock('@/libs/assets/icons', () => ({
CheckCircleIcon: () => <svg aria-label="지원 조건 충족" />,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분도 다른 테스트와 동일하게 ReactNode를 type import해서 사용하도록 정리하겠습니다.

}));

describe('Table', () => {
it('renders recurring dates, descriptions, and badges for schedule rows', () => {
render(
<Table
isApplicant={false}
dataList={[
{
recurring_date: '매주 목요일',
text: '정기 세션 참여',
badge: '필수',
},
{
recurring_date: '격주 토요일',
text: '네트워킹 참여',
highlight: true,
},
]}
/>,
);

expect(screen.getByText('매주 목요일')).toBeInTheDocument();
expect(screen.getByText('정기 세션 참여')).toBeInTheDocument();
expect(screen.getByText('필수')).toBeInTheDocument();
expect(screen.getByText('격주 토요일')).toBeInTheDocument();
expect(screen.getByText('네트워킹 참여')).toBeInTheDocument();
expect(screen.getByTestId('glow-area')).toHaveTextContent('네트워킹 참여');
});

it('renders applicant rows with check icons and without schedule metadata', () => {
render(
<Table
isApplicant
dataList={[
{
recurring_date: '표시되지 않는 날짜',
text: '개발 경험이 있는 분',
badge: '표시되지 않는 배지',
},
]}
/>,
);

expect(screen.getByLabelText('지원 조건 충족')).toBeInTheDocument();
expect(screen.getByText('개발 경험이 있는 분')).toBeInTheDocument();
expect(screen.queryByText('표시되지 않는 날짜')).not.toBeInTheDocument();
expect(screen.queryByText('표시되지 않는 배지')).not.toBeInTheDocument();
});
});
Loading