102 lines
4.9 KiB
TypeScript
102 lines
4.9 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';
|
|
|
|
vi.mock('../../src/renderer/inbox/api.js', () => ({
|
|
inboxApi: {
|
|
loadOllamaSettings: vi.fn(async () => ({ endpoint: 'http://localhost:11434', model: 'gemma2:2b' })),
|
|
saveOllamaSettings: vi.fn(async () => ({ ok: true })),
|
|
ollamaRecheck: vi.fn(async () => ({ ok: true })),
|
|
getSettings: vi.fn(async () => ({ ai_enabled: true })),
|
|
setAiEnabled: vi.fn(async () => ({ ok: true })),
|
|
getDisabledCount: vi.fn(async () => 0),
|
|
enqueueDisabled: vi.fn(async () => ({ count: 0 })),
|
|
// v0.3.1 Cut F — VisionSection 이 AiProviderSection 에 마운트되어 호출.
|
|
getVisionModels: vi.fn(async () => ({ models: [], at: null, selected: null })),
|
|
setVisionModel: vi.fn(async () => ({ ok: true as const })),
|
|
refreshVisionCache: vi.fn(async () => ({ ok: true as const, models: [] }))
|
|
}
|
|
}));
|
|
|
|
import { AiProviderSection } from '../../src/renderer/inbox/components/settings/AiProviderSection';
|
|
|
|
describe('AiProviderSection', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
cleanup();
|
|
});
|
|
|
|
it('loads current settings on mount', async () => {
|
|
render(<AiProviderSection />);
|
|
expect(await screen.findByDisplayValue('http://localhost:11434')).toBeInTheDocument();
|
|
expect(screen.getByDisplayValue('gemma2:2b')).toBeInTheDocument();
|
|
});
|
|
|
|
it('rejects invalid endpoint URL', async () => {
|
|
render(<AiProviderSection />);
|
|
await screen.findByDisplayValue('http://localhost:11434');
|
|
const input = screen.getByLabelText(/Endpoint/);
|
|
fireEvent.change(input, { target: { value: 'not-a-url' } });
|
|
fireEvent.click(screen.getByRole('button', { name: /저장/ }));
|
|
expect(await screen.findByText(/올바른 URL/)).toBeInTheDocument();
|
|
});
|
|
|
|
it('"지금 재확인" calls ollamaRecheck and shows result', async () => {
|
|
const { inboxApi } = await import('../../src/renderer/inbox/api.js');
|
|
render(<AiProviderSection />);
|
|
await screen.findByDisplayValue('http://localhost:11434');
|
|
fireEvent.click(screen.getByRole('button', { name: /지금 재확인/ }));
|
|
expect(inboxApi.ollamaRecheck).toHaveBeenCalled();
|
|
});
|
|
|
|
// v0.2.9 Cut B Task 15 — AI 자동 처리 토글 + OFF 안내문.
|
|
it('renders AI 자동 처리 toggle (default true)', async () => {
|
|
const { inboxApi } = await import('../../src/renderer/inbox/api.js');
|
|
vi.mocked(inboxApi.getSettings).mockResolvedValue({ ai_enabled: true } as never);
|
|
render(<AiProviderSection />);
|
|
const toggle = await screen.findByLabelText(/AI 자동 처리 사용/);
|
|
expect((toggle as HTMLInputElement).checked).toBe(true);
|
|
});
|
|
|
|
it('toggling calls setAiEnabled', async () => {
|
|
const { inboxApi } = await import('../../src/renderer/inbox/api.js');
|
|
vi.mocked(inboxApi.getSettings).mockResolvedValue({ ai_enabled: true } as never);
|
|
vi.mocked(inboxApi.setAiEnabled).mockResolvedValue({ ok: true } as never);
|
|
render(<AiProviderSection />);
|
|
const toggle = await screen.findByLabelText(/AI 자동 처리 사용/);
|
|
fireEvent.click(toggle);
|
|
await waitFor(() => expect(inboxApi.setAiEnabled).toHaveBeenCalledWith(false));
|
|
});
|
|
|
|
it('shows OFF state explanation when ai_enabled=false', async () => {
|
|
const { inboxApi } = await import('../../src/renderer/inbox/api.js');
|
|
vi.mocked(inboxApi.getSettings).mockResolvedValue({ ai_enabled: false } as never);
|
|
render(<AiProviderSection />);
|
|
await screen.findByLabelText(/AI 자동 처리 사용/);
|
|
expect(screen.getByText(/원문만 저장 모드/)).toBeInTheDocument();
|
|
expect(screen.getByRole('link', { name: /ollama\.com|설치/ })).toBeInTheDocument();
|
|
});
|
|
|
|
// v0.2.9 Cut B Task 16 — ON 전환 후 disabled 메모 처리 prompt + 버튼.
|
|
it('shows disabled count + 처리 버튼 when ai_enabled=true and disabledCount > 0', async () => {
|
|
const { inboxApi } = await import('../../src/renderer/inbox/api.js');
|
|
vi.mocked(inboxApi.getSettings).mockResolvedValue({ ai_enabled: true } as never);
|
|
vi.mocked(inboxApi.getDisabledCount).mockResolvedValue(5);
|
|
render(<AiProviderSection />);
|
|
await screen.findByText(/5건/);
|
|
expect(screen.getByRole('button', { name: /지금 모두 처리/ })).toBeInTheDocument();
|
|
});
|
|
|
|
it('clicking 처리 버튼 calls enqueueDisabled', async () => {
|
|
const { inboxApi } = await import('../../src/renderer/inbox/api.js');
|
|
vi.mocked(inboxApi.getSettings).mockResolvedValue({ ai_enabled: true } as never);
|
|
vi.mocked(inboxApi.getDisabledCount).mockResolvedValue(3);
|
|
vi.mocked(inboxApi.enqueueDisabled).mockResolvedValue({ count: 3 } as never);
|
|
render(<AiProviderSection />);
|
|
await screen.findByText(/3건/);
|
|
fireEvent.click(screen.getByRole('button', { name: /지금 모두 처리/ }));
|
|
await waitFor(() => expect(inboxApi.enqueueDisabled).toHaveBeenCalled());
|
|
});
|
|
});
|