taskKanbanCardExecute.test.tsx2.7 KBView on GitHub /**
* `TaskKanbanCard`'s Execute handler takes the task instead of closing over it.
*
* Not a cosmetic signature: the card is `memo`'d, and a callback built inside the board's `map` is
* a fresh prop identity on every render, so the memo never bailed out and every card on the board
* re-rendered whenever any board state changed. Mid-drag that state changes every frame, which is
* what put visible distance between the cursor and the card under it.
*
* Passing the task through is what lets one shared function serve every card — the per-card
* decision left is only WHETHER to offer Execute, which is a boolean. Pinned here because the
* whole point is that callers stop building a closure per card, and nothing else would notice.
*/
import { fireEvent, render, screen } from '@testing-library/react';
jest.mock('@dnd-kit/sortable', () => ({
useSortable: () => ({
attributes: {},
listeners: {},
setNodeRef: () => {},
transform: null,
transition: undefined,
isDragging: false,
isSorting: false,
}),
}));
jest.mock('@/modules/userTasks/hooks/use-prefetch-task-threads', () => ({
usePrefetchTaskThreads: () => () => {},
usePreloadTaskThreads: () => {},
}));
jest.mock('@/modules/userTasks/hooks/use-task-is-executing', () => ({
useTaskIsExecuting: () => false,
}));
jest.mock('@/modules/store', () => ({
useIsTaskSelected: () => false,
}));
jest.mock('@/modules/conversationsPage/components/ConversationCompanyAvatar', () => ({
ConversationCompanyAvatar: () => null,
}));
import { TaskKanbanCard, type TaskCardTask } from '@/modules/userTasks/components/TaskKanbanCard';
const TASK: TaskCardTask = {
id: 'task-1',
description: 'Send pricing recap',
conversationId: 'conv-1',
status: 'todo',
};
const OTHER: TaskCardTask = { ...TASK, id: 'task-2', description: 'Book the follow-up' };
describe('TaskKanbanCard — Execute', () => {
it('hands the handler its own task, so two cards can share one function', () => {
const onExecute = jest.fn();
render(
<>
<TaskKanbanCard task={TASK} draggable={false} onExecute={onExecute} />
<TaskKanbanCard task={OTHER} draggable={false} onExecute={onExecute} />
</>,
);
const buttons = screen.getAllByRole('button', { name: /create draft/i });
expect(buttons).toHaveLength(2);
fireEvent.click(buttons[1]!);
expect(onExecute).toHaveBeenCalledWith(OTHER);
fireEvent.click(buttons[0]!);
expect(onExecute).toHaveBeenLastCalledWith(TASK);
});
it('renders no Execute affordance when the surface does not offer one', () => {
render(<TaskKanbanCard task={TASK} draggable={false} />);
expect(screen.queryByRole('button', { name: /create draft/i })).toBeNull();
});
});