Skip to content

Commit e78b544

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 e78b544

File tree

3 files changed

+222
-9
lines changed

3 files changed

+222
-9
lines changed

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

0 commit comments

Comments
 (0)