Files
kandev/apps/web/hooks/use-task-workflow-move.test.ts
T
José Almeida e2c076c71c feat(web): improve mobile task navigation (#1769)
* feat(web): improve mobile task and kanban navigation

* feat(web): refine mobile task navigation

* fix(web): address mobile navigation review feedback

* test(web): cover task title field modes

* fix(web): keep mobile board navigator open across workflows

* fix(web): address mobile workflow review feedback
2026-07-18 02:28:29 +01:00

106 lines
3.5 KiB
TypeScript

import { act, renderHook } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { useTaskWorkflowMove } from "@/hooks/use-task-workflow-move";
import { bulkMoveSelectedTasks } from "@/lib/api";
const mockToast = vi.fn();
vi.mock("@/components/toast-provider", () => ({
useToast: () => ({ toast: mockToast }),
}));
vi.mock("@/lib/api", () => ({
bulkMoveSelectedTasks: vi.fn(),
}));
const mockBulkMoveSelectedTasks = vi.mocked(bulkMoveSelectedTasks);
const TASK_1 = "task-1";
const TASK_2 = "task-2";
const TARGET_WORKFLOW_ID = "wf-2";
const TARGET_STEP_ID = "step-2";
describe("useTaskWorkflowMove", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("dedupes and filters task ids before moving", async () => {
mockBulkMoveSelectedTasks.mockResolvedValue({ moved_count: 2 });
const { result } = renderHook(() => useTaskWorkflowMove());
await act(async () => {
await result.current([TASK_1, "", TASK_1, TASK_2], TARGET_WORKFLOW_ID, TARGET_STEP_ID);
});
expect(mockBulkMoveSelectedTasks).toHaveBeenCalledWith({
task_ids: [TASK_1, TASK_2],
target_workflow_id: TARGET_WORKFLOW_ID,
target_step_id: TARGET_STEP_ID,
});
});
it("does nothing for an empty task id list", async () => {
const { result } = renderHook(() => useTaskWorkflowMove());
await act(async () => {
await result.current([""], TARGET_WORKFLOW_ID, TARGET_STEP_ID);
});
expect(mockBulkMoveSelectedTasks).not.toHaveBeenCalled();
expect(mockToast).not.toHaveBeenCalled();
});
it("shows workflow and same-workflow step success messages", async () => {
mockBulkMoveSelectedTasks
.mockResolvedValueOnce({ moved_count: 1 })
.mockResolvedValueOnce({ moved_count: 2 })
.mockResolvedValueOnce({ moved_count: 0 })
.mockResolvedValueOnce({ moved_count: 1 });
const { result } = renderHook(() => useTaskWorkflowMove());
await act(async () => {
await result.current([TASK_1], TARGET_WORKFLOW_ID, TARGET_STEP_ID);
await result.current([TASK_1, TASK_2], TARGET_WORKFLOW_ID, TARGET_STEP_ID);
await result.current([TASK_1], TARGET_WORKFLOW_ID, TARGET_STEP_ID);
await result.current([TASK_1], TARGET_WORKFLOW_ID, TARGET_STEP_ID, "step");
});
expect(mockToast).toHaveBeenNthCalledWith(1, {
title: "Moved task to workflow",
description: "Switch to the destination workflow to see it.",
variant: "success",
});
expect(mockToast).toHaveBeenNthCalledWith(2, {
title: "Moved 2 tasks to workflow",
description: "Switch to the destination workflow to see them.",
variant: "success",
});
expect(mockToast).toHaveBeenNthCalledWith(3, {
title: "Moved 0 tasks to workflow",
description: "Switch to the destination workflow to see them.",
variant: "success",
});
expect(mockToast).toHaveBeenNthCalledWith(4, {
title: "Moved task to step",
description: "The task is now in the selected step.",
variant: "success",
});
});
it("shows an error toast and rethrows move failures", async () => {
const error = new Error("cannot move running task");
mockBulkMoveSelectedTasks.mockRejectedValue(error);
const { result } = renderHook(() => useTaskWorkflowMove());
await expect(result.current([TASK_1], TARGET_WORKFLOW_ID, TARGET_STEP_ID)).rejects.toThrow(
error,
);
expect(mockToast).toHaveBeenCalledWith({
title: "Failed to move task",
description: "cannot move running task",
variant: "error",
});
});
});