Files
inkling/tests/unit/MoveStatusModal.test.tsx
th-kim0823 274c171ee8 fix(lifecycle): NoteStatus 의 archived 제거 — MoveStatusModal/classifyStatus/store 정리
- NoteStatus 에서 'archived' 제거 (active | completed | trashed 3분기)
- MoveStatusModal ALL_STATUSES 에서 'archived' 제거 + statusLabel switch 정리
- classifyStatus VALID/FALLBACK/PROMPT 에서 archived 제거 → completed fallback
- inboxApi IPC set-status VALID 배열에서 archived 제거, classify-status fallback → completed
- store InboxView 에서 'archived' 제거, InboxCounts.archived 제거, archived: 0 spread 제거
- ImportService.applySyncFromDir — 기존 파일의 status=archived 를 completed 로 coerce
- 영향 받는 tests 13개 파일 모두 update (archived → completed, 없어진 UI 옵션 제거)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 11:03:09 +09:00

175 lines
5.5 KiB
TypeScript

// @vitest-environment jsdom
import { describe, it, expect, vi, beforeEach } from 'vitest';
import '@testing-library/jest-dom/vitest';
import { render, screen, fireEvent, cleanup, waitFor } from '@testing-library/react';
const { mockSetStatus, mockClassify } = vi.hoisted(() => ({
mockSetStatus: vi.fn(async () => ({ ok: true as const })),
mockClassify: vi.fn(async () => ({
recommended: 'completed' as const,
rationale: '결재 끝'
}))
}));
vi.mock('../../src/renderer/inbox/api.js', () => ({
inboxApi: {
setStatus: mockSetStatus,
classifyStatus: mockClassify
}
}));
import { MoveStatusModal } from '../../src/renderer/inbox/components/MoveStatusModal';
describe('MoveStatusModal', () => {
beforeEach(() => {
vi.clearAllMocks();
cleanup();
});
it('renders reason textarea + 3 buttons + AI classify button (v0.4 — 보관 제거)', () => {
render(
<MoveStatusModal
noteId="n1"
rawText="t"
summary=""
currentStatus="active"
onClose={vi.fn()}
onMoved={vi.fn()}
/>
);
expect(screen.getByRole('textbox')).toBeInTheDocument();
expect(screen.getByRole('button', { name: '완료' })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: '보관' })).toBeNull();
expect(screen.getByRole('button', { name: '휴지통' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /AI 자동 분류/ })).toBeInTheDocument();
});
it('clicking 완료 calls setStatus with reason', async () => {
const onMoved = vi.fn();
render(
<MoveStatusModal
noteId="n1"
rawText="t"
summary=""
currentStatus="active"
onClose={vi.fn()}
onMoved={onMoved}
/>
);
fireEvent.change(screen.getByRole('textbox'), { target: { value: '결재 끝' } });
fireEvent.click(screen.getByRole('button', { name: '완료' }));
await waitFor(() => {
expect(mockSetStatus).toHaveBeenCalledWith('n1', 'completed', '결재 끝');
expect(onMoved).toHaveBeenCalledWith('completed', '결재 끝');
});
});
it('AI 자동 분류 → recommendation 표시 + 확정 → setStatus', async () => {
const onMoved = vi.fn();
render(
<MoveStatusModal
noteId="n1"
rawText="t"
summary=""
currentStatus="active"
onClose={vi.fn()}
onMoved={onMoved}
/>
);
fireEvent.change(screen.getByRole('textbox'), { target: { value: '결재 끝' } });
fireEvent.click(screen.getByRole('button', { name: /AI 자동 분류/ }));
await screen.findByText(/AI 추천/);
expect(screen.getByText(/이유: 결재 끝/)).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: /확정/ }));
await waitFor(() => expect(onMoved).toHaveBeenCalledWith('completed', '결재 끝'));
});
it('currentStatus=completed → Inbox/휴지통 노출, 완료/보관 미노출 (v0.4 — 보관 제거)', () => {
render(
<MoveStatusModal
noteId="n1"
rawText="t"
summary=""
currentStatus="completed"
onClose={vi.fn()}
onMoved={vi.fn()}
/>
);
expect(screen.getByRole('button', { name: 'Inbox' })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: '보관' })).toBeNull();
expect(screen.getByRole('button', { name: '휴지통' })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: '완료' })).toBeNull();
});
// v0.4 Task 16 — currentStatus=archived 는 NoteStatus 에서 제거됨. 테스트 제거.
it('currentStatus=trashed → Inbox/완료 노출, 휴지통/보관 미노출 (v0.4 — 보관 제거)', () => {
render(
<MoveStatusModal
noteId="n1"
rawText="t"
summary=""
currentStatus="trashed"
onClose={vi.fn()}
onMoved={vi.fn()}
/>
);
expect(screen.getByRole('button', { name: 'Inbox' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '완료' })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: '보관' })).toBeNull();
expect(screen.queryByRole('button', { name: '휴지통' })).toBeNull();
});
it('Escape key → onClose 호출', () => {
const onClose = vi.fn();
render(
<MoveStatusModal
noteId="n1"
rawText="t"
summary=""
currentStatus="active"
onClose={onClose}
onMoved={vi.fn()}
/>
);
fireEvent.keyDown(document, { key: 'Escape' });
expect(onClose).toHaveBeenCalled();
});
it('overlay 클릭 → onClose, modal body 클릭 → 무반응', () => {
const onClose = vi.fn();
render(
<MoveStatusModal
noteId="n1"
rawText="t"
summary=""
currentStatus="active"
onClose={onClose}
onMoved={vi.fn()}
/>
);
// body 클릭 (textarea) → onClose 호출 안 됨
fireEvent.click(screen.getByRole('textbox'));
expect(onClose).not.toHaveBeenCalled();
// overlay (dialog) 클릭 → onClose
fireEvent.click(screen.getByRole('dialog', { name: '이동' }));
expect(onClose).toHaveBeenCalled();
});
it('빈 사유 → null reason 전달', async () => {
const onMoved = vi.fn();
render(
<MoveStatusModal
noteId="n1"
rawText="t"
summary=""
currentStatus="active"
onClose={vi.fn()}
onMoved={onMoved}
/>
);
fireEvent.click(screen.getByRole('button', { name: '완료' }));
await waitFor(() => expect(mockSetStatus).toHaveBeenCalledWith('n1', 'completed', null));
});
});