From 381c6cbfcd17739c0f131f66ce6b7886276a1eb1 Mon Sep 17 00:00:00 2001 From: Gabriel Sancho Date: Thu, 26 Mar 2026 20:28:53 -0300 Subject: [PATCH] test(frontend): add failing tests for step 1 - entity task workflow --- .../__tests__/pages/DashboardPage.test.tsx | 25 ++++ .../src/__tests__/pages/EntitiesPage.test.tsx | 9 ++ .../__tests__/pages/EntityDetailPage.test.tsx | 118 ++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 frontend/src/__tests__/pages/EntityDetailPage.test.tsx diff --git a/frontend/src/__tests__/pages/DashboardPage.test.tsx b/frontend/src/__tests__/pages/DashboardPage.test.tsx index 85aa259..e925511 100644 --- a/frontend/src/__tests__/pages/DashboardPage.test.tsx +++ b/frontend/src/__tests__/pages/DashboardPage.test.tsx @@ -44,4 +44,29 @@ describe('DashboardPage', () => { expect(screen.getByText(/Memo/i)).toBeInTheDocument() }) }) + + it('should_renderScheduledTasks_when_entitiesHaveSchedule', async () => { + vi.mocked(entitiesApi.getEntities).mockResolvedValue([ + { + id: '1', + name: 'Entity A', + email: 'a@a.com', + jobTitle: 'Ops', + personality: '', + scheduleCron: '0 9 * * 1', + contextWindowDays: 3, + active: true, + createdAt: '', + }, + ]) + vi.mocked(logsApi.getLogs).mockResolvedValue([]) + + render(, { wrapper }) + + await waitFor(() => { + expect(screen.getByText(/Scheduled Tasks/i)).toBeInTheDocument() + expect(screen.getByText(/Entity A/i)).toBeInTheDocument() + expect(screen.getByText(/0 9 \* \* 1/i)).toBeInTheDocument() + }) + }) }) \ No newline at end of file diff --git a/frontend/src/__tests__/pages/EntitiesPage.test.tsx b/frontend/src/__tests__/pages/EntitiesPage.test.tsx index 21bbe3d..1321703 100644 --- a/frontend/src/__tests__/pages/EntitiesPage.test.tsx +++ b/frontend/src/__tests__/pages/EntitiesPage.test.tsx @@ -57,4 +57,13 @@ describe('EntitiesPage', () => { expect(entitiesApi.deleteEntity).toHaveBeenCalledWith('entity-1') }) }) + + it('should_renderDetailLink_when_entitiesLoaded', async () => { + vi.mocked(entitiesApi.getEntities).mockResolvedValue([mockEntity]) + render(, { wrapper }) + + await waitFor(() => { + expect(screen.getByRole('link', { name: /open details/i })).toHaveAttribute('href', '/entities/entity-1') + }) + }) }) diff --git a/frontend/src/__tests__/pages/EntityDetailPage.test.tsx b/frontend/src/__tests__/pages/EntityDetailPage.test.tsx new file mode 100644 index 0000000..6a0b0ec --- /dev/null +++ b/frontend/src/__tests__/pages/EntityDetailPage.test.tsx @@ -0,0 +1,118 @@ +import { render, screen, waitFor, fireEvent } from '@testing-library/react' +import { describe, it, expect, vi } from 'vitest' +import { MemoryRouter, Route, Routes } from 'react-router-dom' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import EntityDetailPage from '@/pages/EntityDetailPage' +import * as entitiesApi from '@/api/entitiesApi' +import * as tasksApi from '@/api/tasksApi' + +vi.mock('@/api/entitiesApi') +vi.mock('@/api/tasksApi') + +const wrapper = ({ children }: { children: React.ReactNode }) => ( + + + + + + + +) + +describe('EntityDetailPage', () => { + it('should_renderEntityAndTasks_when_pageLoads', async () => { + vi.mocked(entitiesApi.getEntity).mockResolvedValue({ + id: 'entity-1', + name: 'Entity A', + email: 'a@a.com', + jobTitle: 'Ops', + personality: 'Formal', + scheduleCron: '0 9 * * 1', + contextWindowDays: 3, + active: true, + createdAt: '', + }) + vi.mocked(tasksApi.getTasksByEntity).mockResolvedValue([ + { + id: 'task-1', + entityId: 'entity-1', + name: 'Weekly Check-in', + prompt: 'Summarize jokes', + scheduleCron: '0 9 * * 1', + emailLookback: 'last_week', + createdAt: '2026-03-26T10:00:00Z', + }, + ]) + + render(, { wrapper }) + + await waitFor(() => { + expect(screen.getByText(/Entity A/i)).toBeInTheDocument() + expect(screen.getByText(/Weekly Check-in/i)).toBeInTheDocument() + expect(screen.getByText(/Last week/i)).toBeInTheDocument() + }) + }) + + it('should_generatePreviewAndCreateTask_when_formSubmitted', async () => { + vi.mocked(entitiesApi.getEntity).mockResolvedValue({ + id: 'entity-1', + name: 'Entity A', + email: 'a@a.com', + jobTitle: 'Ops', + personality: 'Formal', + scheduleCron: '0 9 * * 1', + contextWindowDays: 3, + active: true, + createdAt: '', + }) + vi.mocked(tasksApi.getTasksByEntity).mockResolvedValue([]) + vi.mocked(tasksApi.generateTaskPreview).mockResolvedValue('SUBJECT: Preview\nBODY:\nFormal nonsense') + vi.mocked(tasksApi.createTask).mockResolvedValue({ + id: 'task-2', + entityId: 'entity-1', + name: 'Morning Blast', + prompt: 'Talk about coffee', + scheduleCron: '0 8 * * 1-5', + emailLookback: 'last_week', + createdAt: '2026-03-26T10:00:00Z', + }) + + render(, { wrapper }) + + await screen.findByRole('button', { name: /new task/i }) + fireEvent.click(screen.getByRole('button', { name: /new task/i })) + + fireEvent.change(screen.getByLabelText(/task name/i), { target: { value: 'Morning Blast' } }) + fireEvent.change(screen.getByLabelText(/task prompt/i), { target: { value: 'Talk about coffee' } }) + fireEvent.change(screen.getByLabelText(/task schedule/i), { target: { value: '0 8 * * 1-5' } }) + + fireEvent.click(screen.getByRole('button', { name: /generate test message/i })) + + await waitFor(() => { + expect(tasksApi.generateTaskPreview).toHaveBeenCalledWith( + expect.objectContaining({ + entityId: 'entity-1', + name: 'Morning Blast', + prompt: 'Talk about coffee', + scheduleCron: '0 8 * * 1-5', + emailLookback: 'last_week', + }) + ) + expect(screen.getByText(/Formal nonsense/i)).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole('button', { name: /create task/i })) + + await waitFor(() => { + expect(tasksApi.createTask).toHaveBeenCalledWith( + expect.objectContaining({ + entityId: 'entity-1', + name: 'Morning Blast', + prompt: 'Talk about coffee', + scheduleCron: '0 8 * * 1-5', + emailLookback: 'last_week', + }) + ) + }) + }) +})