feat(frontend): generate task previews with local ollama

Replace the local preview stub with a real Ollama-backed test message flow using the configured local model.

Show the exact final prompt live on create and edit task pages, render generated output below it, and cover the integration with frontend tests.
This commit is contained in:
2026-03-27 01:28:29 -03:00
parent 1a7f5d706a
commit a83ea85857
6 changed files with 394 additions and 42 deletions

View File

@@ -66,6 +66,10 @@ describe('EditTaskPage', () => {
vi.clearAllMocks()
vi.mocked(entitiesApi.getEntity).mockResolvedValue(mockEntity)
vi.mocked(tasksApi.getTask).mockResolvedValue(mockTask)
vi.mocked(tasksApi.buildTaskPreviewPrompt).mockImplementation(
(entity, task) =>
`PROMPT FOR ${entity.name}: ${task.name} | ${task.prompt} | ${task.scheduleCron} | ${task.emailLookback}`
)
vi.mocked(tasksApi.activateTask).mockResolvedValue({ ...mockTask, active: true })
vi.mocked(tasksApi.inactivateTask).mockResolvedValue({ ...mockTask, active: false })
vi.mocked(tasksApi.deleteTask).mockResolvedValue(undefined)
@@ -112,11 +116,19 @@ describe('EditTaskPage', () => {
fireEvent.click(screen.getByRole('button', { name: /Weekdays/i }))
fireEvent.change(screen.getByLabelText(/^Hour$/i), { target: { value: '8' } })
expect(screen.getByText(/Final Prompt/i)).toBeInTheDocument()
expect(
screen.getByText(
'PROMPT FOR Entity A: Daily Check-in | Ask about ceremonial coffee | 0 8 * * 1-5 | last_day'
)
).toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: /generate test message/i }))
await waitFor(() => {
expect(tasksApi.generateTaskPreview).toHaveBeenCalled()
expect(vi.mocked(tasksApi.generateTaskPreview).mock.calls[0][0]).toEqual(
expect(vi.mocked(tasksApi.buildTaskPreviewPrompt)).toHaveBeenCalledWith(
mockEntity,
expect.objectContaining({
entityId: 'entity-1',
name: 'Daily Check-in',
@@ -125,6 +137,18 @@ describe('EditTaskPage', () => {
emailLookback: 'last_day',
})
)
expect(vi.mocked(tasksApi.generateTaskPreview).mock.calls[0][0]).toEqual(
expect.objectContaining({
entity: mockEntity,
task: expect.objectContaining({
entityId: 'entity-1',
name: 'Daily Check-in',
prompt: 'Ask about ceremonial coffee',
scheduleCron: '0 8 * * 1-5',
emailLookback: 'last_day',
}),
})
)
expect(screen.getByText(/Formal nonsense/i)).toBeInTheDocument()
})
@@ -145,6 +169,23 @@ describe('EditTaskPage', () => {
})
})
it('should_showReadablePreviewError_when_generationFails', async () => {
vi.mocked(tasksApi.generateTaskPreview).mockRejectedValue(
new Error('Unable to generate a test message from the local model. Connection refused')
)
renderPage()
await screen.findByRole('link', { name: /back to entity a/i })
fireEvent.click(screen.getByRole('button', { name: /generate test message/i }))
await waitFor(() => {
expect(
screen.getByText(/Unable to generate a test message from the local model. Connection refused/i)
).toBeInTheDocument()
})
})
it('should_renderActivateButton_when_taskIsInactive', async () => {
renderPage({ task: { ...mockTask, active: false } })