Files
kandev/apps/web/hooks/use-command-panel-task-results.test.ts
T
João Salavisa b8e0dd7dc5 feat(web): give tasks their own command palette scope (#2979)
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 21:31:26 +00:00

90 lines
3.1 KiB
TypeScript

import { renderHook, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
const listTasksByWorkspace = vi.fn();
vi.mock("@/lib/api", () => ({
listTasksByWorkspace: (...args: unknown[]) => listTasksByWorkspace(...args),
}));
import { useInlineTaskSearchEffect } from "./use-command-panel-task-results";
import type { Task } from "@/lib/types/http";
import { taskId, workflowId, workspaceId } from "@/lib/types/ids";
function task(id: string, title: string): Task {
return {
id: taskId(id),
workspace_id: workspaceId("workspace-1"),
workflow_id: workflowId("workflow-1"),
workflow_step_id: "step-1",
position: 0,
title,
description: "",
state: "IN_PROGRESS",
priority: "medium",
created_at: "2026-08-24T09:00:00Z",
updated_at: "2026-08-24T09:00:00Z",
};
}
const STEPS = [{ id: "step-1", position: 0, show_in_command_panel: true }];
function harness(setTaskResults: (t: Task[]) => void, setIsSearching: (s: boolean) => void) {
return renderHook(
({ mode }: { mode: "commands" | "search-tasks" }) =>
useInlineTaskSearchEffect({
mode,
search: "",
open: true,
workspaceId: "workspace-1",
steps: STEPS,
setTaskResults,
setIsSearching,
}),
{ initialProps: { mode: "commands" } as { mode: "commands" | "search-tasks" } },
);
}
afterEach(() => {
listTasksByWorkspace.mockReset();
});
describe("useInlineTaskSearchEffect", () => {
it("caps the commands scope at a five-row preview and the tasks scope at twenty", async () => {
const many = Array.from({ length: 20 }, (_, i) => task(`task-${i}`, `Task ${i}`));
listTasksByWorkspace.mockResolvedValue({ tasks: many });
const setTaskResults = vi.fn();
const { rerender } = harness(setTaskResults, vi.fn());
await waitFor(() => expect(setTaskResults).toHaveBeenCalledWith(many.slice(0, 5)));
setTaskResults.mockClear();
rerender({ mode: "search-tasks" });
await waitFor(() => expect(setTaskResults).toHaveBeenCalledWith(many));
});
it("drops the previous scope's results when the scope changes", async () => {
listTasksByWorkspace.mockResolvedValue({ tasks: [task("task-1", "Only task")] });
const setTaskResults = vi.fn();
const { rerender } = harness(setTaskResults, vi.fn());
await waitFor(() => expect(setTaskResults).toHaveBeenCalled());
setTaskResults.mockClear();
rerender({ mode: "search-tasks" });
// The five-row preview must not stand in as the tasks scope's full result
// set (nor suppress its loading state) while the wider request is in flight.
expect(setTaskResults).toHaveBeenNthCalledWith(1, []);
});
it("keeps results when the scope is unchanged", async () => {
listTasksByWorkspace.mockResolvedValue({ tasks: [task("task-1", "Only task")] });
const setTaskResults = vi.fn();
const { rerender } = harness(setTaskResults, vi.fn());
await waitFor(() => expect(setTaskResults).toHaveBeenCalled());
setTaskResults.mockClear();
rerender({ mode: "commands" });
expect(setTaskResults).not.toHaveBeenCalledWith([]);
});
});