// @vitest-environment jsdom import { describe, it, expect, vi, beforeEach } from 'vitest'; import '@testing-library/jest-dom/vitest'; import { render, screen, fireEvent, cleanup } from '@testing-library/react'; const accept = vi.fn(async () => {}); const snooze = vi.fn(async () => {}); const dismiss = vi.fn(async () => {}); let mockState: { promotionCandidates: Array<{ tag: string; noteIds: string[]; suggestedName: string }>; acceptPromotion: typeof accept; snoozePromotion: typeof snooze; dismissPromotion: typeof dismiss; } = { promotionCandidates: [], acceptPromotion: accept, snoozePromotion: snooze, dismissPromotion: dismiss }; vi.mock('../../src/renderer/inbox/store.js', () => ({ useInbox: (selector?: (s: typeof mockState) => unknown) => selector ? selector(mockState) : mockState })); import { PromotionBanner } from '../../src/renderer/inbox/components/PromotionBanner'; describe('PromotionBanner', () => { beforeEach(() => { cleanup(); accept.mockClear(); snooze.mockClear(); dismiss.mockClear(); mockState = { promotionCandidates: [], acceptPromotion: accept, snoozePromotion: snooze, dismissPromotion: dismiss }; }); it('candidates 비어있으면 null', () => { const { container } = render(); expect(container.firstChild).toBeNull(); }); it('첫 candidate 의 tag/suggestedName 표시', () => { mockState.promotionCandidates = [{ tag: 'mlx-ops', noteIds: ['n1', 'n2', 'n3'], suggestedName: 'Mlx Ops' }]; render(); expect(screen.getByText('mlx-ops')).toBeInTheDocument(); expect(screen.getByText('Mlx Ops')).toBeInTheDocument(); }); it('수락 클릭 → 편집 모드 → 만들기 → acceptPromotion 호출', () => { mockState.promotionCandidates = [{ tag: 'mlx-ops', noteIds: ['n1', 'n2', 'n3'], suggestedName: 'Mlx Ops' }]; render(); fireEvent.click(screen.getByRole('button', { name: '수락' })); fireEvent.click(screen.getByRole('button', { name: '만들기' })); expect(accept).toHaveBeenCalledWith('mlx-ops', 'Mlx Ops', expect.any(String)); }); it('나중에 클릭 → snoozePromotion 호출', () => { mockState.promotionCandidates = [{ tag: 'mlx-ops', noteIds: ['n1', 'n2', 'n3'], suggestedName: 'Mlx Ops' }]; render(); fireEvent.click(screen.getByRole('button', { name: '나중에' })); expect(snooze).toHaveBeenCalled(); }); it('숨기기 클릭 → dismissPromotion(tag) 호출', () => { mockState.promotionCandidates = [{ tag: 'mlx-ops', noteIds: ['n1', 'n2', 'n3'], suggestedName: 'Mlx Ops' }]; render(); fireEvent.click(screen.getByRole('button', { name: '숨기기' })); expect(dismiss).toHaveBeenCalledWith('mlx-ops'); }); it('편집 모드: 이름 빈 시 만들기 disabled', () => { mockState.promotionCandidates = [{ tag: 'mlx-ops', noteIds: ['n1', 'n2', 'n3'], suggestedName: 'Mlx Ops' }]; render(); fireEvent.click(screen.getByRole('button', { name: '수락' })); fireEvent.change(screen.getByLabelText('노트북 이름'), { target: { value: '' } }); expect(screen.getByRole('button', { name: '만들기' })).toBeDisabled(); }); });