Skip to content

Commit 63ea37c

Browse files
committed
test(Tasks): Add Recur, Priority, and Reports toggle tests
- Add feature-specific tests for editing flows - Move data-testid from SelectTrigger to Select for testability - Update Select mock to support dynamic test-ids - Add ResizeObserver mock
1 parent 5b33b58 commit 63ea37c

3 files changed

Lines changed: 230 additions & 9 deletions

File tree

frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ export const AddTaskdialog = ({
272272
setNewTask({ ...newTask, project: value });
273273
}
274274
}}
275+
data-testid="project-select"
275276
>
276277
<SelectTrigger
277278
onKeyDown={(e) => {
@@ -284,7 +285,6 @@ export const AddTaskdialog = ({
284285
}}
285286
ref={(element) => (inputRefs.current.project = element)}
286287
id="project"
287-
data-testid="project-select"
288288
>
289289
<SelectValue
290290
placeholder={

frontend/src/components/HomeComponents/Tasks/TaskDialog.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,7 @@ export const TaskDialog = ({
10011001
editedPriority: value,
10021002
})
10031003
}
1004+
data-testid="priority-select"
10041005
>
10051006
<SelectTrigger className="flex-grow mr-2">
10061007
<SelectValue placeholder="Select priority" />
@@ -1096,11 +1097,9 @@ export const TaskDialog = ({
10961097
onSaveProject(task, project);
10971098
}
10981099
}}
1100+
data-testid="project-select"
10991101
>
1100-
<SelectTrigger
1101-
id="project"
1102-
data-testid="project-select"
1103-
>
1102+
<SelectTrigger id="project">
11041103
<SelectValue
11051104
placeholder={
11061105
uniqueProjects.length
@@ -1374,6 +1373,7 @@ export const TaskDialog = ({
13741373
onValueChange={(value) =>
13751374
onUpdateState({ editedRecur: value })
13761375
}
1376+
data-testid="recur-select"
13771377
>
13781378
<SelectTrigger className="flex-grow">
13791379
<SelectValue placeholder="Select recurrence" />

frontend/src/components/HomeComponents/Tasks/__tests__/Tasks.test.tsx

Lines changed: 225 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ jest.mock('@/components/ui/multi-select', () => ({
5858

5959
jest.mock('@/components/ui/select', () => {
6060
return {
61-
Select: ({ children, onValueChange, value }: any) => {
61+
Select: ({ children, onValueChange, value, ...props }: any) => {
6262
return (
6363
<select
64-
data-testid="project-select"
64+
{...props}
6565
value={value}
6666
onChange={(e) => onValueChange?.(e.target.value)}
6767
>
@@ -182,6 +182,12 @@ jest.mock('../TaskSkeleton', () => {
182182

183183
global.fetch = jest.fn().mockResolvedValue({ ok: true });
184184

185+
global.ResizeObserver = class ResizeObserver {
186+
observe() {}
187+
unobserve() {}
188+
disconnect() {}
189+
};
190+
185191
describe('Tasks Component', () => {
186192
const localStorageMock = (() => {
187193
let store: { [key: string]: string } = {};
@@ -1229,7 +1235,7 @@ describe('Tasks Component', () => {
12291235
const editButton = within(priorityRow).getByLabelText('edit');
12301236
fireEvent.click(editButton);
12311237

1232-
const select = within(priorityRow).getByTestId('project-select');
1238+
const select = within(priorityRow).getByTestId('priority-select');
12331239
fireEvent.change(select, { target: { value: 'H' } });
12341240

12351241
const saveButton = screen.getByLabelText('save');
@@ -1339,7 +1345,7 @@ describe('Tasks Component', () => {
13391345
const editButton = within(recurRow).getByLabelText('edit');
13401346
fireEvent.click(editButton);
13411347

1342-
const select = within(recurRow).getByTestId('project-select');
1348+
const select = within(recurRow).getByTestId('recur-select');
13431349
fireEvent.change(select, { target: { value: 'weekly' } });
13441350

13451351
const saveButton = screen.getByLabelText('save');
@@ -1737,4 +1743,219 @@ describe('Tasks Component', () => {
17371743
expect(task1Row).toBeInTheDocument();
17381744
});
17391745
});
1746+
1747+
describe('Recur Editing', () => {
1748+
test('does not save when recur is set to "none"', async () => {
1749+
render(<Tasks {...mockProps} />);
1750+
1751+
await screen.findByText('Task 12');
1752+
fireEvent.click(screen.getByText('Task 12'));
1753+
1754+
await waitFor(() => {
1755+
expect(screen.getByText('Recur:')).toBeInTheDocument();
1756+
});
1757+
1758+
const recurLabel = screen.getByText('Recur:');
1759+
const recurRow = recurLabel.closest('tr') as HTMLElement;
1760+
const editButton = within(recurRow).getByLabelText('edit');
1761+
1762+
fireEvent.click(editButton);
1763+
1764+
const select = within(recurRow).getByTestId('recur-select');
1765+
fireEvent.change(select, { target: { value: 'none' } });
1766+
1767+
const saveButton = screen.getByLabelText('save');
1768+
fireEvent.click(saveButton);
1769+
1770+
const hooks = require('../hooks');
1771+
expect(hooks.editTaskOnBackend).not.toHaveBeenCalled();
1772+
});
1773+
1774+
test('saves recur when a valid value is selected', async () => {
1775+
render(<Tasks {...mockProps} />);
1776+
1777+
await screen.findByText('Task 12');
1778+
fireEvent.click(screen.getByText('Task 12'));
1779+
1780+
await waitFor(() => {
1781+
expect(screen.getByText('Recur:')).toBeInTheDocument();
1782+
});
1783+
1784+
const recurLabel = screen.getByText('Recur:');
1785+
const recurRow = recurLabel.closest('tr') as HTMLElement;
1786+
const editButton = within(recurRow).getByLabelText('edit');
1787+
1788+
fireEvent.click(editButton);
1789+
1790+
const select = within(recurRow).getByTestId('recur-select');
1791+
fireEvent.change(select, { target: { value: 'daily' } });
1792+
1793+
const saveButton = screen.getByLabelText('save');
1794+
fireEvent.click(saveButton);
1795+
1796+
const hooks = require('../hooks');
1797+
expect(hooks.editTaskOnBackend).toHaveBeenCalled();
1798+
});
1799+
1800+
test('does not save when recur is empty string', async () => {
1801+
render(<Tasks {...mockProps} />);
1802+
1803+
await screen.findByText('Task 12');
1804+
fireEvent.click(screen.getByText('Task 12'));
1805+
1806+
await waitFor(() => {
1807+
expect(screen.getByText('Recur:')).toBeInTheDocument();
1808+
});
1809+
1810+
const recurLabel = screen.getByText('Recur:');
1811+
const recurRow = recurLabel.closest('tr') as HTMLElement;
1812+
const editButton = within(recurRow).getByLabelText('edit');
1813+
fireEvent.click(editButton);
1814+
1815+
const select = within(recurRow).getByTestId('recur-select');
1816+
fireEvent.change(select, { target: { value: '' } });
1817+
const saveButton = within(recurRow).getByLabelText('save');
1818+
fireEvent.click(saveButton);
1819+
1820+
const hooks = require('../hooks');
1821+
expect(hooks.editTaskOnBackend).not.toHaveBeenCalled();
1822+
});
1823+
});
1824+
1825+
describe('Priority Editing', () => {
1826+
test('saving priority calls modifyTaskOnBackend with correct value', async () => {
1827+
render(<Tasks {...mockProps} />);
1828+
1829+
await screen.findByText('Task 12');
1830+
fireEvent.click(screen.getByText('Task 12'));
1831+
1832+
await waitFor(() => {
1833+
expect(screen.getByText('Priority:')).toBeInTheDocument();
1834+
});
1835+
1836+
const priorityLabel = screen.getByText('Priority:');
1837+
const priorityRow = priorityLabel.closest('tr') as HTMLElement;
1838+
const editButton = within(priorityRow).getByLabelText('edit');
1839+
fireEvent.click(editButton);
1840+
1841+
const select = within(priorityRow).getByTestId('priority-select');
1842+
fireEvent.change(select, { target: { value: 'H' } });
1843+
1844+
const saveButton = screen.getByLabelText('save');
1845+
fireEvent.click(saveButton);
1846+
1847+
await waitFor(() => {
1848+
const hooks = require('../hooks');
1849+
expect(hooks.modifyTaskOnBackend).toHaveBeenCalledWith(
1850+
expect.objectContaining({ priority: 'H' })
1851+
);
1852+
});
1853+
});
1854+
1855+
test('saving "NONE" priority sends empty string to backend', async () => {
1856+
render(<Tasks {...mockProps} />);
1857+
1858+
await screen.findByText('Task 12');
1859+
fireEvent.click(screen.getByText('Task 12'));
1860+
1861+
await waitFor(() => {
1862+
expect(screen.getByText('Priority:')).toBeInTheDocument();
1863+
});
1864+
1865+
const priorityLabel = screen.getByText('Priority:');
1866+
const priorityRow = priorityLabel.closest('tr') as HTMLElement;
1867+
const editButton = within(priorityRow).getByLabelText('edit');
1868+
fireEvent.click(editButton);
1869+
1870+
const select = within(priorityRow).getByTestId('priority-select');
1871+
fireEvent.change(select, { target: { value: 'NONE' } });
1872+
1873+
const saveButton = screen.getByLabelText('save');
1874+
fireEvent.click(saveButton);
1875+
1876+
await waitFor(() => {
1877+
const hooks = require('../hooks');
1878+
expect(hooks.modifyTaskOnBackend).toHaveBeenCalledWith(
1879+
expect.objectContaining({
1880+
priority: '',
1881+
})
1882+
);
1883+
});
1884+
});
1885+
1886+
test('shows error toast when priority save fails', async () => {
1887+
const { toast } = require('react-toastify');
1888+
const hooks = require('../hooks');
1889+
1890+
hooks.modifyTaskOnBackend.mockRejectedValueOnce(
1891+
new Error('Network error')
1892+
);
1893+
1894+
render(<Tasks {...mockProps} />);
1895+
await screen.findByText('Task 12');
1896+
1897+
fireEvent.click(screen.getByText('Task 12'));
1898+
1899+
await waitFor(() => {
1900+
expect(screen.getByText('Priority:')).toBeInTheDocument();
1901+
});
1902+
1903+
const priorityLabel = screen.getByText('Priority:');
1904+
const priorityRow = priorityLabel.closest('tr') as HTMLElement;
1905+
1906+
const editButton = within(priorityRow).getByLabelText('edit');
1907+
fireEvent.click(editButton);
1908+
1909+
const select = within(priorityRow).getByTestId('priority-select');
1910+
fireEvent.change(select, { target: { value: 'H' } });
1911+
1912+
const saveButton = within(priorityRow).getByLabelText('save');
1913+
fireEvent.click(saveButton);
1914+
1915+
await waitFor(() => {
1916+
expect(toast.error).toHaveBeenCalledWith(
1917+
expect.stringContaining('Failed to update priority')
1918+
);
1919+
});
1920+
});
1921+
});
1922+
1923+
describe('Reports Toggle', () => {
1924+
test('clicking "Show Reports" button switches view from tasks to reports', async () => {
1925+
render(<Tasks {...mockProps} />);
1926+
1927+
await screen.findByText('Task 1');
1928+
1929+
expect(screen.getByText('Here are')).toBeInTheDocument();
1930+
1931+
const toggleBtn = screen.getByRole('button', { name: /show reports/i });
1932+
fireEvent.click(toggleBtn);
1933+
1934+
expect(
1935+
screen.getByRole('button', { name: /show tasks/i })
1936+
).toBeInTheDocument();
1937+
});
1938+
1939+
test('clicking "Show Tasks" returns to task list view', async () => {
1940+
render(<Tasks {...mockProps} />);
1941+
await screen.findByText('Task 1');
1942+
1943+
fireEvent.click(screen.getByRole('button', { name: /show reports/i }));
1944+
fireEvent.click(screen.getByRole('button', { name: /show tasks/i }));
1945+
1946+
expect(screen.getByText('Task 1')).toBeInTheDocument();
1947+
});
1948+
1949+
test('hotkeys are disabled when reports view is shown', async () => {
1950+
render(<Tasks {...mockProps} />);
1951+
await screen.findByText('Task 1');
1952+
1953+
fireEvent.click(screen.getByRole('button', { name: /show reports/i }));
1954+
fireEvent.keyDown(window, { key: 'a' });
1955+
1956+
expect(
1957+
screen.queryByText(/fill in the details below/i)
1958+
).not.toBeInTheDocument();
1959+
});
1960+
});
17401961
});

0 commit comments

Comments
 (0)