lib/action-menu-search.test.ts
Rama main · réplica de lectura del repositorio original.
import { describe, expect, it } from "vitest";
import { searchActionTree } from "@/lib/action-menu-search";
type Action = { id: string; label: string; keywords?: string[]; disabled?: boolean; searchFallback?: boolean; children?: Action[] };
const tree: Action[] = [
{ id: "copy", label: "Copy identifier" },
{
id: "relations",
label: "Relations",
keywords: ["link"],
children: [
{ id: "blocked-by", label: "Blocked by", keywords: ["relation"] },
{ id: "blocks", label: "Blocks", keywords: ["relation"] },
],
},
{
id: "color",
label: "Color",
disabled: true,
children: [{ id: "text", label: "Text", children: [{ id: "red", label: "Red" }] }],
},
];
const ids = (query: string) => searchActionTree(tree, query).map(({ action, path }) => [...path, action.id].join(" > "));
describe("searchActionTree", () => {
it("returns the top level unchanged without a query", () => {
expect(ids(" ")).toEqual(["copy", "relations", "color"]);
});
it("finds an entry inside a submenu with the path that leads to it", () => {
const [match] = searchActionTree(tree, "blocked");
expect(match.action.id).toBe("blocked-by");
expect(match.path).toEqual(["Relations"]);
});
it("keeps a matching branch and lists only the nested entries that match on their own", () => {
expect(ids("relation")).toEqual(["relations", "Relations > blocked-by", "Relations > blocks"]);
expect(ids("link")).toEqual(["relations"]);
});
it("walks every level but never returns a nested branch, only its entries", () => {
expect(ids("text")).toEqual([]);
expect(ids("red")).toEqual(["Color > Text > red"]);
});
it("matches the start of a word, not its middle", () => {
expect(ids("by")).toEqual(["Relations > blocked-by"]);
expect(ids("ocked")).toEqual([]);
expect(ids("identifier")).toEqual(["copy"]);
});
it("disables an entry whose branch is disabled", () => {
expect(searchActionTree(tree, "red")[0].disabled).toBe(true);
expect(searchActionTree(tree, "blocks")[0].disabled).toBe(false);
});
it("keeps a fallback entry first without a query and last behind every match", () => {
const withFallback: Action[] = [{ id: "numo", label: "Create with Numo", searchFallback: true }, ...tree];
const fallbackIds = (query: string) => searchActionTree(withFallback, query).map(({ action, path }) => [...path, action.id].join(" > "));
expect(fallbackIds("")).toEqual(["numo", "copy", "relations", "color"]);
expect(fallbackIds("blocked")).toEqual(["Relations > blocked-by", "numo"]);
expect(fallbackIds("create")).toEqual(["numo"]);
expect(fallbackIds("nothing matches")).toEqual(["numo"]);
});
});