From f6e4b1de7ec51118eb8eaccad00ea5f634994b21 Mon Sep 17 00:00:00 2001 From: Samuel James Date: Sat, 5 Sep 2026 15:28:01 +0000 Subject: [PATCH] Clean up: Remove src files --- frontend/src/App.test.tsx | 305 -------------------------------------- 1 file changed, 305 deletions(-) delete mode 100644 frontend/src/App.test.tsx diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx deleted file mode 100644 index 5a4436d..0000000 --- a/frontend/src/App.test.tsx +++ /dev/null @@ -1,305 +0,0 @@ -import React from 'react'; -import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import App from './App'; -import { api } from './services/api'; -import { Task, User } from './types'; - -jest.mock('./services/api', () => ({ - ...jest.requireActual('./services/api'), - api: { - getCurrentUser: jest.fn(), - getUsers: jest.fn(), - getTasks: jest.fn(), - createTask: jest.fn(), - updateTask: jest.fn(), - deleteTask: jest.fn(), - }, -})); - -const mockApi = api as jest.Mocked; - -const sarah: User = { id: 1, username: 'sarah.chen', full_name: 'Sarah Chen' }; -const alex: User = { id: 2, username: 'alex.rivera', full_name: 'Alex Rivera' }; - -const myTask: Task = { - id: 1, - title: 'Q4 Marketing Strategy', - description: 'Outline key campaigns and budget', - assigned_to: sarah, - status: 'open', - priority: 'medium', - due_date: '2026-10-20', - created_at: '2026-09-01T10:00:00Z', - updated_at: '2026-09-01T10:00:00Z', -}; - -const blockedTask: Task = { - id: 2, - title: 'API Integration Debugging', - description: 'Fixing errors in payment gateway', - assigned_to: alex, - status: 'blocked', - priority: 'high', - due_date: '2026-10-18', - created_at: '2026-09-02T10:00:00Z', - updated_at: '2026-09-02T10:00:00Z', -}; - -/** The desktop table and the mobile cards are both in the DOM (CSS picks one), - * so row-level assertions are scoped to the table. */ -const table = () => within(screen.getByRole('table')); - -const renderApp = async (): Promise => { - render(); - await waitFor(() => expect(screen.getByRole('table')).toBeInTheDocument()); -}; - -beforeEach(() => { - jest.clearAllMocks(); - mockApi.getCurrentUser.mockResolvedValue(sarah); - mockApi.getUsers.mockResolvedValue([sarah, alex]); - mockApi.getTasks.mockResolvedValue([myTask, blockedTask]); -}); - -describe('loading the dashboard', () => { - it('shows a loading state, then the team task table', async () => { - render(); - - expect(screen.getByRole('status')).toHaveTextContent(/loading/i); - - await waitFor(() => expect(screen.getByRole('table')).toBeInTheDocument()); - expect(table().getByText('Q4 Marketing Strategy')).toBeInTheDocument(); - expect(table().getByText('API Integration Debugging')).toBeInTheDocument(); - }); - - it('offers a retry when the request fails, and recovers on retry', async () => { - mockApi.getTasks.mockRejectedValueOnce(new Error('Network request failed')); - - render(); - - const alert = await screen.findByRole('alert'); - expect(alert).toHaveTextContent('Network request failed'); - - await userEvent.click(screen.getByRole('button', { name: /try again/i })); - - await waitFor(() => expect(screen.getByRole('table')).toBeInTheDocument()); - expect(table().getByText('Q4 Marketing Strategy')).toBeInTheDocument(); - }); -}); - -describe('filtering', () => { - it('limits My Tasks to the signed-in user', async () => { - await renderApp(); - - await userEvent.click(screen.getByRole('tab', { name: /my tasks/i })); - - expect(table().getByText('Q4 Marketing Strategy')).toBeInTheDocument(); - expect(table().queryByText('API Integration Debugging')).not.toBeInTheDocument(); - }); - - it('explains why work is blocked in the Blocked view', async () => { - await renderApp(); - - await userEvent.click(screen.getByRole('tab', { name: /blocked/i })); - - expect(screen.getByRole('heading', { name: /these tasks are blocked/i })).toBeInTheDocument(); - expect(table().getByText('API Integration Debugging')).toBeInTheDocument(); - expect(table().queryByText('Q4 Marketing Strategy')).not.toBeInTheDocument(); - }); - - it('reports an empty filter without pretending there are no tasks at all', async () => { - mockApi.getTasks.mockResolvedValue([myTask]); - await renderApp(); - - await userEvent.click(screen.getByRole('tab', { name: /blocked/i })); - - expect(screen.getByRole('heading', { name: /no blocked tasks/i })).toBeInTheDocument(); - }); -}); - -describe('creating a task', () => { - it('creates and lists a task in three steps', async () => { - const created: Task = { - ...myTask, - id: 3, - title: 'Update client onboarding documentation', - description: '', - assigned_to: alex, - priority: 'high', - due_date: '2026-11-01', - }; - mockApi.createTask.mockResolvedValue(created); - - await renderApp(); - - // 1. open the form - await userEvent.click(screen.getByRole('button', { name: 'Create task' })); - expect(screen.getByRole('dialog', { name: /create new task/i })).toBeInTheDocument(); - - // 2. fill in the required fields - fireEvent.change(screen.getByLabelText(/task title/i), { - target: { value: 'Update client onboarding documentation' }, - }); - fireEvent.change(screen.getByLabelText(/assignee/i), { target: { value: '2' } }); - fireEvent.change(screen.getByLabelText(/due date/i), { target: { value: '2026-11-01' } }); - fireEvent.change(screen.getByLabelText(/priority/i), { target: { value: 'high' } }); - - // 3. save - await userEvent.click(screen.getByRole('button', { name: /save task/i })); - - await waitFor(() => - expect(mockApi.createTask).toHaveBeenCalledWith({ - title: 'Update client onboarding documentation', - description: '', - assignee_id: 2, - status: 'open', - priority: 'high', - due_date: '2026-11-01', - }) - ); - - await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); - expect(table().getByText('Update client onboarding documentation')).toBeInTheDocument(); - }); - - it('explains what to fix instead of submitting an incomplete task', async () => { - await renderApp(); - - await userEvent.click(screen.getByRole('button', { name: 'Create task' })); - await userEvent.click(screen.getByRole('button', { name: /save task/i })); - - expect(await screen.findByText(/enter a task title/i)).toBeInTheDocument(); - expect(screen.getByText(/choose the team member who owns this task/i)).toBeInTheDocument(); - expect(screen.getByText(/pick a due date/i)).toBeInTheDocument(); - expect(screen.getByLabelText(/task title/i)).toHaveAttribute('aria-invalid', 'true'); - expect(mockApi.createTask).not.toHaveBeenCalled(); - expect(screen.getByRole('dialog')).toBeInTheDocument(); - }); - - it('keeps the form and its input on screen when saving fails', async () => { - mockApi.createTask.mockRejectedValue(new Error('Connection lost before the task was saved.')); - - await renderApp(); - - await userEvent.click(screen.getByRole('button', { name: 'Create task' })); - fireEvent.change(screen.getByLabelText(/task title/i), { target: { value: 'Draft that must survive' } }); - fireEvent.change(screen.getByLabelText(/assignee/i), { target: { value: '2' } }); - fireEvent.change(screen.getByLabelText(/due date/i), { target: { value: '2026-11-01' } }); - await userEvent.click(screen.getByRole('button', { name: /save task/i })); - - expect( - await screen.findByText('Connection lost before the task was saved.') - ).toBeInTheDocument(); - expect(screen.getByRole('dialog')).toBeInTheDocument(); - expect(screen.getByLabelText(/task title/i)).toHaveValue('Draft that must survive'); - }); -}); - -describe('updating and deleting', () => { - it('changes status straight from the list', async () => { - mockApi.updateTask.mockResolvedValue({ ...myTask, status: 'in_progress' }); - - await renderApp(); - - fireEvent.change(table().getByLabelText('Status for Q4 Marketing Strategy'), { - target: { value: 'in_progress' }, - }); - - await waitFor(() => expect(mockApi.updateTask).toHaveBeenCalledWith(1, { status: 'in_progress' })); - expect(await screen.findByText(/is now in progress/i)).toBeInTheDocument(); - }); - - it('leaves the task untouched and says so when an update fails', async () => { - mockApi.updateTask.mockRejectedValue(new Error('Could not reach the server.')); - - await renderApp(); - - fireEvent.change(table().getByLabelText('Status for Q4 Marketing Strategy'), { - target: { value: 'blocked' }, - }); - - expect(await screen.findByText('Could not reach the server.')).toBeInTheDocument(); - expect(table().getByLabelText('Status for Q4 Marketing Strategy')).toHaveValue('open'); - }); - - it('opens the edit dialog prefilled from the row', async () => { - await renderApp(); - - await userEvent.click(table().getByRole('button', { name: 'Edit Q4 Marketing Strategy' })); - - expect(screen.getByRole('dialog', { name: /edit task/i })).toBeInTheDocument(); - expect(screen.getByLabelText(/task title/i)).toHaveValue('Q4 Marketing Strategy'); - expect(screen.getByLabelText(/assignee/i)).toHaveValue('1'); - }); - - it('asks before deleting and does nothing if the user backs out', async () => { - const confirmSpy = jest.spyOn(window, 'confirm').mockReturnValue(false); - - await renderApp(); - await userEvent.click(table().getByRole('button', { name: 'Delete Q4 Marketing Strategy' })); - - expect(confirmSpy).toHaveBeenCalled(); - expect(mockApi.deleteTask).not.toHaveBeenCalled(); - expect(table().getByText('Q4 Marketing Strategy')).toBeInTheDocument(); - - confirmSpy.mockRestore(); - }); - - it('deletes once confirmed', async () => { - const confirmSpy = jest.spyOn(window, 'confirm').mockReturnValue(true); - mockApi.deleteTask.mockResolvedValue(undefined); - - await renderApp(); - await userEvent.click(table().getByRole('button', { name: 'Delete Q4 Marketing Strategy' })); - - await waitFor(() => expect(mockApi.deleteTask).toHaveBeenCalledWith(1)); - await waitFor(() => expect(table().queryByText('Q4 Marketing Strategy')).not.toBeInTheDocument()); - - confirmSpy.mockRestore(); - }); -}); - -describe('keyboard and screen reader support', () => { - it('labels the filter tabs as a tablist with counts', async () => { - await renderApp(); - - const tabs = screen.getAllByRole('tab'); - expect(tabs).toHaveLength(3); - expect(screen.getByRole('tab', { name: /all tasks, 2 tasks/i })).toHaveAttribute( - 'aria-selected', - 'true' - ); - }); - - it('moves between filter tabs with the arrow keys', async () => { - await renderApp(); - - const allTab = screen.getByRole('tab', { name: /all tasks/i }); - allTab.focus(); - fireEvent.keyDown(allTab, { key: 'ArrowRight' }); - - await waitFor(() => - expect(screen.getByRole('tab', { name: /blocked/i })).toHaveAttribute('aria-selected', 'true') - ); - }); - - it('closes the dialog on Escape without saving', async () => { - await renderApp(); - - await userEvent.click(screen.getByRole('button', { name: 'Create task' })); - fireEvent.keyDown(screen.getByRole('dialog'), { key: 'Escape' }); - - await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); - expect(mockApi.createTask).not.toHaveBeenCalled(); - }); - - it('exposes every column header and a skip link', async () => { - await renderApp(); - - ['Priority', 'Task Name', 'Assignee', 'Status', 'Due Date', 'Actions'].forEach((header) => { - expect(table().getByRole('columnheader', { name: header })).toBeInTheDocument(); - }); - expect(screen.getByRole('link', { name: /skip to task list/i })).toBeInTheDocument(); - }); -});