-
Notifications
You must be signed in to change notification settings - Fork 74
test(Tasks): Add Priority tests with shared test utilities #461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| import { | ||
| render, | ||
| screen, | ||
| fireEvent, | ||
| waitFor, | ||
| within, | ||
| } from '@testing-library/react'; | ||
| import { Tasks } from '../../Tasks'; | ||
| import { openTaskDialog, getRowAndClickEdit } from '../test-utils/helper'; | ||
| import { createMockProps } from '../test-utils/setup'; | ||
|
|
||
| jest.mock('react-toastify', () => | ||
| require('../test-utils/setup').createToastMock() | ||
| ); | ||
| jest.mock('../../tasks-utils', () => | ||
| require('../test-utils/setup').createTasksUtilsMock() | ||
| ); | ||
| jest.mock('../../hooks', () => | ||
| require('../test-utils/setup').createHooksMock() | ||
| ); | ||
| jest.mock( | ||
| '@/components/ui/select', | ||
| () => require('../test-utils/setup').selectMock | ||
| ); | ||
|
|
||
| describe('Priority Editing', () => { | ||
| const { toast } = require('react-toastify'); | ||
| const hooks = require('../../hooks'); | ||
|
|
||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| }); | ||
|
|
||
| test('should save selected priority value to backend', async () => { | ||
| render(<Tasks {...createMockProps()} />); | ||
| await openTaskDialog('Task 12'); | ||
|
|
||
| const priorityRow = getRowAndClickEdit('Priority:'); | ||
| const select = within(priorityRow).getByTestId('project-select'); | ||
| fireEvent.change(select, { target: { value: 'H' } }); | ||
|
|
||
| const saveButton = screen.getByLabelText('save'); | ||
| fireEvent.click(saveButton); | ||
|
|
||
| expect(hooks.modifyTaskOnBackend).toHaveBeenCalledWith( | ||
| expect.objectContaining({ priority: 'H' }) | ||
| ); | ||
| }); | ||
|
|
||
| test('should send empty string when NONE priority is selected', async () => { | ||
| render(<Tasks {...createMockProps()} />); | ||
| await openTaskDialog('Task 12'); | ||
| const priorityRow = getRowAndClickEdit('Priority:'); | ||
|
|
||
| const select = within(priorityRow).getByTestId('project-select'); | ||
| fireEvent.change(select, { target: { value: 'NONE' } }); | ||
|
|
||
| const saveButton = screen.getByLabelText('save'); | ||
| fireEvent.click(saveButton); | ||
|
|
||
| expect(hooks.modifyTaskOnBackend).toHaveBeenCalledWith( | ||
| expect.objectContaining({ | ||
| priority: '', | ||
| }) | ||
| ); | ||
| }); | ||
|
|
||
| test('should show error toast when save fails', async () => { | ||
| hooks.modifyTaskOnBackend.mockRejectedValueOnce(new Error('Network error')); | ||
|
|
||
| render(<Tasks {...createMockProps()} />); | ||
| await openTaskDialog('Task 12'); | ||
| const priorityRow = getRowAndClickEdit('Priority:'); | ||
|
|
||
| const select = within(priorityRow).getByTestId('project-select'); | ||
| fireEvent.change(select, { target: { value: 'H' } }); | ||
|
|
||
| const saveButton = screen.getByLabelText('save'); | ||
| fireEvent.click(saveButton); | ||
|
|
||
| await waitFor(() => { | ||
| expect(toast.error).toHaveBeenCalledWith( | ||
| expect.stringContaining('Failed to update priority') | ||
| ); | ||
| }); | ||
| }); | ||
| }); |
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reusable helper functions like opening dialogs and clicking edit buttons, keeps test code DRY and readable |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import { screen, fireEvent, within } from '@testing-library/react'; | ||
|
|
||
| export const openTaskDialog = async (taskDescription: string) => { | ||
| await screen.findByText(taskDescription); | ||
| fireEvent.click(screen.getByText(taskDescription)); | ||
| await screen.findByText('Description:'); | ||
| }; | ||
|
|
||
| export const getRowAndClickEdit = (fieldLabel: string) => { | ||
| const row = screen.getByText(fieldLabel).closest('tr') as HTMLElement; | ||
| const editButton = within(row).getByLabelText('edit'); | ||
| fireEvent.click(editButton); | ||
| return row; | ||
| }; |
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shared mocks and test data for tests, will add more utilities as we add more test files (Recur, Reports, etc.) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| export const createMockProps = () => ({ | ||
| origin: '', | ||
| email: 'test@example.com', | ||
| encryptionSecret: 'mockEncryptionSecret', | ||
| UUID: 'mockUUID', | ||
| isLoading: false, | ||
| setIsLoading: jest.fn(), | ||
| }); | ||
|
|
||
| export const mockTasks = [ | ||
| ...Array.from({ length: 12 }, (_, i) => ({ | ||
| id: i + 1, | ||
| description: `Task ${i + 1}`, | ||
| status: 'pending', | ||
| project: i % 2 === 0 ? 'ProjectA' : 'ProjectB', | ||
| tags: i % 3 === 0 ? ['tag1'] : ['tag2'], | ||
| uuid: `uuid-${i + 1}`, | ||
| due: i === 0 ? '20200101T120000Z' : undefined, | ||
| })), | ||
| { | ||
| id: 13, | ||
| description: | ||
| 'Task 13: Prepare quarterly financial analysis report for review', | ||
| status: 'pending', | ||
| project: 'Finance', | ||
| tags: ['report', 'analysis'], | ||
| uuid: 'uuid-corp-1', | ||
| }, | ||
| { | ||
| id: 14, | ||
| description: 'Task 14: Schedule client onboarding meeting with Sales team', | ||
| status: 'pending', | ||
| project: 'Sales', | ||
| tags: ['meeting', 'client'], | ||
| uuid: 'uuid-corp-2', | ||
| }, | ||
| { | ||
| id: 15, | ||
| description: | ||
| 'Task 15: Draft technical documentation for API integration module', | ||
| status: 'pending', | ||
| project: 'Engineering', | ||
| tags: ['documentation', 'api'], | ||
| uuid: 'uuid-corp-3', | ||
| }, | ||
| { | ||
| id: 16, | ||
| description: 'Completed Task 1', | ||
| status: 'completed', | ||
| project: 'ProjectA', | ||
| tags: ['completed'], | ||
| uuid: 'uuid-completed-1', | ||
| }, | ||
| { | ||
| id: 17, | ||
| description: 'Deleted Task 1', | ||
| status: 'deleted', | ||
| project: 'ProjectB', | ||
| tags: ['deleted'], | ||
| uuid: 'uuid-deleted-1', | ||
| }, | ||
| ]; | ||
|
|
||
| export const createToastMock = () => ({ | ||
| toast: { | ||
| success: jest.fn(), | ||
| error: jest.fn(), | ||
| }, | ||
| }); | ||
|
|
||
| export const createTasksUtilsMock = () => { | ||
| const originalModule = jest.requireActual('../../tasks-utils'); | ||
| return { | ||
| ...originalModule, | ||
| markTaskAsCompleted: jest.fn(), | ||
| bulkMarkTasksAsCompleted: jest.fn().mockResolvedValue(true), | ||
| markTaskAsDeleted: jest.fn(), | ||
| bulkMarkTasksAsDeleted: jest.fn().mockResolvedValue(true), | ||
| getTimeSinceLastSync: jest | ||
| .fn() | ||
| .mockReturnValue('Last updated 5 minutes ago'), | ||
| hashKey: jest.fn((key: string) => `mockHashedKey-${key}`), | ||
| getPinnedTasks: jest.fn().mockReturnValue(new Set()), | ||
| togglePinnedTask: jest.fn(), | ||
| }; | ||
| }; | ||
|
|
||
| export const createHooksMock = () => ({ | ||
| TasksDatabase: jest.fn(() => ({ | ||
| tasks: { | ||
| where: jest.fn(() => ({ | ||
| equals: jest.fn(() => ({ | ||
| toArray: jest.fn().mockResolvedValue(mockTasks), | ||
| delete: jest.fn().mockResolvedValue(undefined), | ||
| })), | ||
| })), | ||
| bulkPut: jest.fn().mockResolvedValue(undefined), | ||
| }, | ||
| transaction: jest.fn(async (_mode, _table, callback) => { | ||
| await callback(); | ||
| return Promise.resolve(); | ||
| }), | ||
| })), | ||
| fetchTaskwarriorTasks: jest.fn().mockResolvedValue([]), | ||
| addTaskToBackend: jest.fn().mockResolvedValue({}), | ||
| editTaskOnBackend: jest.fn().mockResolvedValue({}), | ||
| modifyTaskOnBackend: jest.fn().mockResolvedValue({}), | ||
| }); | ||
|
|
||
| export const selectMock = { | ||
| Select: ({ children, onValueChange, value }: any) => ( | ||
| <select | ||
| data-testid="project-select" | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. test-id as per your suggestion |
||
| value={value} | ||
| onChange={(e) => onValueChange?.(e.target.value)} | ||
| > | ||
| {children} | ||
| </select> | ||
| ), | ||
| SelectTrigger: ({ children }: any) => children, | ||
| SelectValue: ({ placeholder }: any) => ( | ||
| <option value="" disabled hidden> | ||
| {placeholder} | ||
| </option> | ||
| ), | ||
| SelectContent: ({ children }: any) => children, | ||
| SelectItem: ({ value, children }: any) => ( | ||
| <option value={value}>{children}</option> | ||
| ), | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated testMatch to only pick up .test.tsx or .spec.tsx files, prevents setup.tsx and helper.ts as test files