Files
fusion/packages/dashboard/app/components/__tests__/InlineCreateCard.test.tsx
Dustin Byrne 40dacb49d3 feat(KB-156): add search input to dependency dropdowns
- Add searchable text input to TaskDetailModal dependency dropdown with filtering by id, title, and description
- Add matching search input to InlineCreateCard dependency dropdown
- Add sticky .dep-dropdown-search CSS with focus styling
- Reset search term when dropdown closes and reopens
- Add tests for search filtering, case-insensitive matching, empty state, and reset behavior
2026-03-28 16:30:23 -04:00

96 lines
3.7 KiB
TypeScript

import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { InlineCreateCard } from "../InlineCreateCard";
import type { Task, Column } from "@kb/core";
// Mock lucide-react
vi.mock("lucide-react", () => ({
Link: () => null,
}));
// Mock the api module
vi.mock("../../api", () => ({
uploadAttachment: vi.fn(),
}));
function renderCard(tasks: Task[] = []) {
const props = {
tasks,
onSubmit: vi.fn().mockResolvedValue({ id: "KB-001" }),
onCancel: vi.fn(),
addToast: vi.fn(),
};
const result = render(<InlineCreateCard {...props} />);
return { ...result, props };
}
describe("InlineCreateCard blur-to-cancel", () => {
it("calls onCancel when focus leaves the card with empty input", () => {
const { props } = renderCard();
const textarea = screen.getByPlaceholderText("What needs to be done?");
textarea.focus();
fireEvent.focusOut(textarea, { relatedTarget: null });
expect(props.onCancel).toHaveBeenCalledTimes(1);
});
it("does NOT call onCancel when focus leaves with non-empty input", () => {
const { props } = renderCard();
const textarea = screen.getByPlaceholderText("What needs to be done?");
fireEvent.change(textarea, { target: { value: "Some task description" } });
fireEvent.focusOut(textarea, { relatedTarget: null });
expect(props.onCancel).not.toHaveBeenCalled();
});
it("does NOT call onCancel when focus moves to another element inside the card", () => {
const { props } = renderCard();
const textarea = screen.getByPlaceholderText("What needs to be done?");
const depsButton = screen.getByText(/Deps/);
textarea.focus();
fireEvent.focusOut(textarea, { relatedTarget: depsButton });
expect(props.onCancel).not.toHaveBeenCalled();
});
it("calls onCancel when blur with only whitespace input", () => {
const { props } = renderCard();
const textarea = screen.getByPlaceholderText("What needs to be done?");
fireEvent.change(textarea, { target: { value: " " } });
fireEvent.focusOut(textarea, { relatedTarget: null });
expect(props.onCancel).toHaveBeenCalledTimes(1);
});
});
describe("InlineCreateCard dependency dropdown search", () => {
const testTasks: Task[] = [
{ id: "KB-001", title: "Fix login", description: "Login page broken", column: "todo" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
{ id: "KB-002", title: "Add dark mode", description: "Theme support", column: "todo" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "2026-02-01T00:00:00Z", updatedAt: "2026-02-01T00:00:00Z" },
{ id: "KB-003", title: "Refactor API", description: "Clean up endpoints", column: "todo" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "2026-03-01T00:00:00Z", updatedAt: "2026-03-01T00:00:00Z" },
];
it("shows search input when dropdown is opened", () => {
renderCard(testTasks);
fireEvent.click(screen.getByText(/Deps/));
const input = document.querySelector(".dep-dropdown-search") as HTMLInputElement;
expect(input).toBeTruthy();
expect(input.placeholder).toBe("Search tasks…");
});
it("filters tasks by search term", () => {
renderCard(testTasks);
fireEvent.click(screen.getByText(/Deps/));
const input = document.querySelector(".dep-dropdown-search") as HTMLInputElement;
fireEvent.change(input, { target: { value: "dark" } });
const items = document.querySelectorAll(".dep-dropdown-item");
expect(items).toHaveLength(1);
expect(items[0].querySelector(".dep-dropdown-id")?.textContent).toBe("KB-002");
});
});