feat(frontend): toggle task activation state
Add task reactivation support to the local task API and update the edit task page to switch between Activate and Inactivate based on the current task state. Keep the separate entity-page inactive-visibility changes out of this commit so they can be reviewed independently.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
activateTask,
|
||||
createTask,
|
||||
deleteTask,
|
||||
getAllTasks,
|
||||
@@ -152,6 +153,24 @@ describe('tasksApi', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('should_markTaskActive_when_activateTaskCalled', async () => {
|
||||
localStorage.setItem('condado:entity-tasks', JSON.stringify([taskOne, taskTwo]))
|
||||
|
||||
const updatedTask = await activateTask('task-2')
|
||||
|
||||
expect(updatedTask).toEqual({
|
||||
...taskTwo,
|
||||
active: true,
|
||||
})
|
||||
expect(JSON.parse(localStorage.getItem('condado:entity-tasks') ?? '[]')).toEqual([
|
||||
taskOne,
|
||||
{
|
||||
...taskTwo,
|
||||
active: true,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('should_removeTask_when_deleteTaskCalled', async () => {
|
||||
localStorage.setItem('condado:entity-tasks', JSON.stringify([taskOne, taskTwo]))
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ import EditTaskPage from '@/pages/EditTaskPage'
|
||||
import * as entitiesApi from '@/api/entitiesApi'
|
||||
import * as tasksApi from '@/api/tasksApi'
|
||||
|
||||
type RenderPageOptions = {
|
||||
task?: typeof mockTask
|
||||
}
|
||||
|
||||
const mockNavigate = vi.fn()
|
||||
vi.mock('react-router-dom', async () => {
|
||||
const actual = await vi.importActual('react-router-dom')
|
||||
@@ -15,7 +19,7 @@ vi.mock('react-router-dom', async () => {
|
||||
vi.mock('@/api/entitiesApi')
|
||||
vi.mock('@/api/tasksApi')
|
||||
|
||||
function renderPage() {
|
||||
function renderPage(options: RenderPageOptions = {}) {
|
||||
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
@@ -27,6 +31,8 @@ function renderPage() {
|
||||
</QueryClientProvider>
|
||||
)
|
||||
|
||||
vi.mocked(tasksApi.getTask).mockResolvedValue(options.task ?? mockTask)
|
||||
|
||||
render(<EditTaskPage />, { wrapper })
|
||||
|
||||
return { queryClient }
|
||||
@@ -57,8 +63,10 @@ const mockTask = {
|
||||
|
||||
describe('EditTaskPage', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.mocked(entitiesApi.getEntity).mockResolvedValue(mockEntity)
|
||||
vi.mocked(tasksApi.getTask).mockResolvedValue(mockTask)
|
||||
vi.mocked(tasksApi.activateTask).mockResolvedValue({ ...mockTask, active: true })
|
||||
vi.mocked(tasksApi.inactivateTask).mockResolvedValue({ ...mockTask, active: false })
|
||||
vi.mocked(tasksApi.deleteTask).mockResolvedValue(undefined)
|
||||
mockNavigate.mockClear()
|
||||
@@ -137,15 +145,47 @@ describe('EditTaskPage', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should_renderActivateButton_when_taskIsInactive', async () => {
|
||||
renderPage({ task: { ...mockTask, active: false } })
|
||||
|
||||
const activateButton = await screen.findByRole('button', { name: /activate/i })
|
||||
|
||||
expect(activateButton).toHaveClass('border-emerald-700')
|
||||
expect(activateButton).toHaveClass('text-emerald-200')
|
||||
expect(screen.queryByRole('button', { name: /^inactivate$/i })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should_inactivateTaskAndNavigateBack_when_inactivateClicked', async () => {
|
||||
const { queryClient } = renderPage()
|
||||
const invalidateQueriesSpy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
await screen.findByRole('link', { name: /back to entity a/i })
|
||||
fireEvent.click(screen.getByRole('button', { name: /inactivate/i }))
|
||||
const inactivateButton = await screen.findByRole('button', { name: /^inactivate$/i })
|
||||
|
||||
expect(inactivateButton).toHaveClass('border-amber-700')
|
||||
expect(inactivateButton).toHaveClass('text-amber-200')
|
||||
|
||||
fireEvent.click(inactivateButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(tasksApi.inactivateTask).toHaveBeenCalledWith('task-1')
|
||||
expect(tasksApi.activateTask).not.toHaveBeenCalled()
|
||||
expect(invalidateQueriesSpy).toHaveBeenCalledWith({ queryKey: ['entity-tasks', 'entity-1'] })
|
||||
expect(invalidateQueriesSpy).toHaveBeenCalledWith({ queryKey: ['entity-task', 'task-1'] })
|
||||
expect(invalidateQueriesSpy).toHaveBeenCalledWith({ queryKey: ['entity-tasks'] })
|
||||
expect(mockNavigate).toHaveBeenCalledWith('/entities/entity-1')
|
||||
})
|
||||
})
|
||||
|
||||
it('should_activateTaskAndNavigateBack_when_activateClicked', async () => {
|
||||
const { queryClient } = renderPage({ task: { ...mockTask, active: false } })
|
||||
const invalidateQueriesSpy = vi.spyOn(queryClient, 'invalidateQueries')
|
||||
|
||||
const activateButton = await screen.findByRole('button', { name: /^activate$/i })
|
||||
fireEvent.click(activateButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(tasksApi.activateTask).toHaveBeenCalledWith('task-1')
|
||||
expect(tasksApi.inactivateTask).not.toHaveBeenCalled()
|
||||
expect(invalidateQueriesSpy).toHaveBeenCalledWith({ queryKey: ['entity-tasks', 'entity-1'] })
|
||||
expect(invalidateQueriesSpy).toHaveBeenCalledWith({ queryKey: ['entity-task', 'task-1'] })
|
||||
expect(invalidateQueriesSpy).toHaveBeenCalledWith({ queryKey: ['entity-tasks'] })
|
||||
|
||||
Reference in New Issue
Block a user