import { fireEvent, render, screen, waitFor, within } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import OrgOverview from './OrgOverview' import type { Org } from '../api/client' const mocks = vi.hoisted(() => ({ getPosts: vi.fn(), getFiles: vi.fn(), getMemos: vi.fn(), })) vi.mock('../api/client', async (importOriginal) => ({ ...(await importOriginal()), getPosts: mocks.getPosts, getFiles: mocks.getFiles, getMemos: mocks.getMemos, })) function org(overrides: Partial = {}): Org { return { id: 1, orgNo: '001', orgName: '국제방송교류재단', status: 'ACTIVE', deptName: '데이터정보화팀', managerName: '송민지', managerTitle: '과장', managerPhone: '02-3475-5434', managerEmail: 'ming@arirang.com', mjDeptName: null, mjManagerName: null, mjManagerPhone: null, mjManagerEmail: null, lawyerName: null, lawyerPhone: null, lawyerEmail: null, lawyerAssignedDate: null, channelIdMj: 'chan-mj', channelIdLaw: 'chan-law', ...overrides, } } beforeEach(() => { mocks.getPosts.mockReset().mockResolvedValue([]) mocks.getFiles.mockReset().mockResolvedValue([]) mocks.getMemos.mockReset().mockResolvedValue([]) }) describe('OrgOverview', () => { it('연번_기관명 헤더와 신청기관 담당자 정보를 보여준다', async () => { render() expect(screen.getByText('001_국제방송교류재단')).toBeTruthy() expect(screen.getByText('데이터정보화팀')).toBeTruthy() expect(screen.getByText('송민지 과장')).toBeTruthy() expect(screen.getByText('02-3475-5434')).toBeTruthy() expect(screen.getByText('ming@arirang.com')).toBeTruthy() await waitFor(() => expect(mocks.getPosts).toHaveBeenCalled()) }) it('문정원 담당자와 담당 변호사 정보가 비어 있으면 표에 -로 표시된다', async () => { render() const rows = screen.getAllByRole('row') const mjRow = rows.find((row) => row.textContent?.includes('문정원 담당자')) const lawyerRow = rows.find((row) => row.textContent?.includes('담당 변호사')) expect(mjRow).toBeTruthy() expect(lawyerRow).toBeTruthy() expect(within(mjRow as HTMLElement).getAllByText('-')).toHaveLength(5) expect(within(lawyerRow as HTMLElement).getAllByText('-')).toHaveLength(5) await waitFor(() => expect(mocks.getPosts).toHaveBeenCalled()) }) it('담당자 3종이 값을 가지고 있으면 표의 각 행에 값이 표시된다', async () => { render( , ) const rows = screen.getAllByRole('row') const applicantRow = rows.find((row) => row.textContent?.includes('신청기관 담당자')) const mjRow = rows.find((row) => row.textContent?.includes('문정원 담당자')) const lawyerRow = rows.find((row) => row.textContent?.includes('담당 변호사')) expect(within(applicantRow as HTMLElement).getByText('데이터정보화팀')).toBeTruthy() expect(within(applicantRow as HTMLElement).getByText('송민지 과장')).toBeTruthy() expect(within(mjRow as HTMLElement).getByText('문화체육관광부 저작권정책과')).toBeTruthy() expect(within(mjRow as HTMLElement).getByText('김문정')).toBeTruthy() expect(within(mjRow as HTMLElement).getByText('02-1234-5678')).toBeTruthy() expect(within(mjRow as HTMLElement).getByText('mj@mcst.go.kr')).toBeTruthy() expect(within(lawyerRow as HTMLElement).getByText('이변호')).toBeTruthy() expect(within(lawyerRow as HTMLElement).getByText('02-9876-5432')).toBeTruthy() expect(within(lawyerRow as HTMLElement).getByText('lawyer@lawfirm.kr')).toBeTruthy() expect(within(lawyerRow as HTMLElement).getByText('2026-07-21')).toBeTruthy() await waitFor(() => expect(mocks.getPosts).toHaveBeenCalled()) }) it('업무단계 12개가 순서대로 보인다', async () => { render() expect(screen.getByText('신청')).toBeTruthy() expect(screen.getByText('변호사 배당')).toBeTruthy() expect(screen.getByText('보고서 작성')).toBeTruthy() await waitFor(() => expect(mocks.getPosts).toHaveBeenCalled()) }) it('기본 탭인 채팅은 자리표시 대신 채널 선택 화면을 보여준다', async () => { render() expect(screen.getByRole('button', { name: '문정원' })).toBeTruthy() await waitFor(() => expect(mocks.getPosts).toHaveBeenCalledWith(1, 'mj')) }) it('탭을 누르면 해당 탭의 자리표시 내용으로 바뀐다', async () => { render() await waitFor(() => expect(mocks.getPosts).toHaveBeenCalled()) fireEvent.click(screen.getByRole('button', { name: '타임라인' })) expect(screen.getByText('타임라인 상세 화면은 추후 확정')).toBeTruthy() expect(screen.queryByRole('button', { name: '문정원' })).toBeNull() }) it('자료 탭을 누르면 자료 목록 화면으로 바뀐다', async () => { render() fireEvent.click(screen.getByRole('button', { name: '자료' })) await waitFor(() => expect(mocks.getFiles).toHaveBeenCalledWith(1, 'mj')) expect(screen.getByRole('button', { name: '새로고침' })).toBeTruthy() }) it('채널이 있으면 Mattermost 열기가 채널 링크로 활성화된다', async () => { render() const link = screen.getByRole('link', { name: 'Mattermost 열기' }) expect(link).toHaveAttribute( 'href', 'https://hub.iten.co.kr/itn-hub/channels/org-001-mj', ) await waitFor(() => expect(mocks.getPosts).toHaveBeenCalled()) }) it('채널이 없으면 Mattermost 열기가 비활성이다', () => { render() expect(screen.queryByRole('link', { name: 'Mattermost 열기' })).toBeNull() expect(screen.getByRole('button', { name: 'Mattermost 열기' })).toBeDisabled() }) it('치안정책연구소는 policy 채널 링크를 쓴다', async () => { render( , ) expect(screen.getByRole('link', { name: 'Mattermost 열기' })).toHaveAttribute( 'href', 'https://hub.iten.co.kr/itn-hub/channels/org-008-policy-mj', ) await waitFor(() => expect(mocks.getPosts).toHaveBeenCalled()) }) it('미구현 동작 버튼들은 비활성이다', async () => { render() expect(screen.getByRole('button', { name: '진행단계 변경' })).toBeDisabled() expect(screen.getByRole('button', { name: '보고서 관리' })).toBeDisabled() await waitFor(() => expect(mocks.getPosts).toHaveBeenCalled()) }) })