Clean up: Remove src files

This commit is contained in:
Samuel James 2026-09-05 15:28:01 +00:00
parent 4a47ed2e6a
commit f6e4b1de7e

View File

@ -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<typeof api>;
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<void> => {
render(<App />);
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(<App />);
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(<App />);
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();
});
});