test(mobile): add note-prompts API client and prompt-store tests (G14)
This commit is contained in:
parent
4fd6994fb0
commit
406153eeda
64
mobile/src/api/note-prompts.test.ts
Normal file
64
mobile/src/api/note-prompts.test.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
||||||
|
|
||||||
|
const fetchMock = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('./client', () => ({
|
||||||
|
getApiClient: () => ({
|
||||||
|
fetch: fetchMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
import {
|
||||||
|
listPromptTemplates,
|
||||||
|
runPrompt,
|
||||||
|
suggestTags,
|
||||||
|
extractFromUrl,
|
||||||
|
copilotTransform,
|
||||||
|
getReadingTime,
|
||||||
|
} from './note-prompts';
|
||||||
|
|
||||||
|
describe('note-prompts API client', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
fetchMock.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('listPromptTemplates returns items', async () => {
|
||||||
|
fetchMock.mockResolvedValue({ items: [{ id: 't1', slug: 'summarize' }] });
|
||||||
|
const result = await listPromptTemplates();
|
||||||
|
expect(result).toEqual([{ id: 't1', slug: 'summarize' }]);
|
||||||
|
expect(fetchMock).toHaveBeenCalledWith('/note-prompts');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runPrompt posts input', async () => {
|
||||||
|
fetchMock.mockResolvedValue({ content: 'Summary', templateSlug: 'summarize' });
|
||||||
|
const result = await runPrompt({ templateId: 'summarize', noteId: 'n1', workspaceId: 'ws1' });
|
||||||
|
expect(result.content).toBe('Summary');
|
||||||
|
expect(fetchMock).toHaveBeenCalledWith('/note-prompts/run', expect.objectContaining({ method: 'POST' }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('suggestTags returns tag array', async () => {
|
||||||
|
fetchMock.mockResolvedValue({ tags: ['tag1', 'tag2'] });
|
||||||
|
const tags = await suggestTags('n1', 'ws1');
|
||||||
|
expect(tags).toEqual(['tag1', 'tag2']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extractFromUrl sends url and workspaceId', async () => {
|
||||||
|
fetchMock.mockResolvedValue({ title: 'Page', content: 'text', url: 'https://example.com', summarized: true });
|
||||||
|
const result = await extractFromUrl('https://example.com', 'ws1');
|
||||||
|
expect(result.title).toBe('Page');
|
||||||
|
expect(fetchMock).toHaveBeenCalledWith('/note-prompts/url-extract', expect.objectContaining({ method: 'POST' }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('copilotTransform sends action and text', async () => {
|
||||||
|
fetchMock.mockResolvedValue({ text: 'Transformed' });
|
||||||
|
const result = await copilotTransform('n1', 'ws1', 'shorten', 'Long text');
|
||||||
|
expect(result).toBe('Transformed');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getReadingTime returns word count and minutes', async () => {
|
||||||
|
fetchMock.mockResolvedValue({ wordCount: 500, readingTimeMinutes: 3 });
|
||||||
|
const result = await getReadingTime('n1', 'ws1');
|
||||||
|
expect(result.wordCount).toBe(500);
|
||||||
|
expect(result.readingTimeMinutes).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
113
mobile/src/store/prompt-store.test.ts
Normal file
113
mobile/src/store/prompt-store.test.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
||||||
|
|
||||||
|
const listPromptTemplatesMock = vi.fn();
|
||||||
|
const runPromptMock = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('../api/note-prompts', () => ({
|
||||||
|
listPromptTemplates: (...args: unknown[]) => listPromptTemplatesMock(...args),
|
||||||
|
runPrompt: (...args: unknown[]) => runPromptMock(...args),
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { usePromptStore } from './prompt-store';
|
||||||
|
|
||||||
|
function resetStore() {
|
||||||
|
usePromptStore.setState({
|
||||||
|
templates: [],
|
||||||
|
isLoading: false,
|
||||||
|
isRunning: false,
|
||||||
|
lastResult: null,
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('prompt-store', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
resetStore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('initial state is empty', () => {
|
||||||
|
const state = usePromptStore.getState();
|
||||||
|
expect(state.templates).toEqual([]);
|
||||||
|
expect(state.isLoading).toBe(false);
|
||||||
|
expect(state.isRunning).toBe(false);
|
||||||
|
expect(state.lastResult).toBeNull();
|
||||||
|
expect(state.error).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fetchTemplates populates templates on success', async () => {
|
||||||
|
listPromptTemplatesMock.mockResolvedValue([
|
||||||
|
{ id: 't1', slug: 'summarize', name: 'Summarize' },
|
||||||
|
]);
|
||||||
|
|
||||||
|
await usePromptStore.getState().fetchTemplates();
|
||||||
|
|
||||||
|
const state = usePromptStore.getState();
|
||||||
|
expect(state.templates).toHaveLength(1);
|
||||||
|
expect(state.templates[0].slug).toBe('summarize');
|
||||||
|
expect(state.isLoading).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fetchTemplates sets error on failure', async () => {
|
||||||
|
listPromptTemplatesMock.mockRejectedValue(new Error('Network error'));
|
||||||
|
|
||||||
|
await usePromptStore.getState().fetchTemplates();
|
||||||
|
|
||||||
|
const state = usePromptStore.getState();
|
||||||
|
expect(state.error).toBe('Network error');
|
||||||
|
expect(state.isLoading).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runPrompt stores result on success', async () => {
|
||||||
|
runPromptMock.mockResolvedValue({
|
||||||
|
content: 'Summary text',
|
||||||
|
templateSlug: 'summarize',
|
||||||
|
outputType: 'new_note',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await usePromptStore.getState().runPrompt({
|
||||||
|
templateId: 'summarize',
|
||||||
|
noteId: 'n1',
|
||||||
|
workspaceId: 'ws1',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(result!.content).toBe('Summary text');
|
||||||
|
const state = usePromptStore.getState();
|
||||||
|
expect(state.lastResult!.content).toBe('Summary text');
|
||||||
|
expect(state.isRunning).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runPrompt sets error on failure', async () => {
|
||||||
|
runPromptMock.mockRejectedValue(new Error('LLM timeout'));
|
||||||
|
|
||||||
|
const result = await usePromptStore.getState().runPrompt({
|
||||||
|
templateId: 'summarize',
|
||||||
|
noteId: 'n1',
|
||||||
|
workspaceId: 'ws1',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
const state = usePromptStore.getState();
|
||||||
|
expect(state.error).toBe('LLM timeout');
|
||||||
|
expect(state.isRunning).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clearResult resets lastResult', async () => {
|
||||||
|
runPromptMock.mockResolvedValue({ content: 'Result', templateSlug: 'x', outputType: 'new_note' });
|
||||||
|
await usePromptStore.getState().runPrompt({ templateId: 'x', noteId: 'n1', workspaceId: 'ws1' });
|
||||||
|
expect(usePromptStore.getState().lastResult).not.toBeNull();
|
||||||
|
|
||||||
|
usePromptStore.getState().clearResult();
|
||||||
|
expect(usePromptStore.getState().lastResult).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clearError resets error', async () => {
|
||||||
|
runPromptMock.mockRejectedValue(new Error('fail'));
|
||||||
|
await usePromptStore.getState().runPrompt({ templateId: 'x', noteId: 'n1', workspaceId: 'ws1' });
|
||||||
|
expect(usePromptStore.getState().error).toBe('fail');
|
||||||
|
|
||||||
|
usePromptStore.getState().clearError();
|
||||||
|
expect(usePromptStore.getState().error).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user