lib/command-palette/registry/providers/MinddyEntityActionsProvider.test.ts
Rama main · réplica de lectura del repositorio original.
import { describe, expect, it, vi } from "vitest";
import type { PaletteItem } from "../../types";
import { createMinddyEntityActionsProvider } from "./MinddyEntityActionsProvider";
const labels = {
copied: "Copied",
linkCopied: "Link copied",
openInNewTab: "Open in a new tab",
copyLink: "Copy link",
newIssue: "New issue",
newObjective: "New objective",
copyProjectKey: "Copy project key",
openObjectives: "Open objectives",
openPages: "Open pages",
openTriage: "Open triage",
openFeedback: "Open feedback",
openProjectSettings: "Open project settings",
viewObjectiveIssues: "View objective issues",
newIssueForObjective: "New issue for this objective",
copyObjectiveName: "Copy objective name",
openProjectBoard: "Open project board",
newIssueInProject: "New issue in this project",
copyPageTitle: "Copy page title",
copyIssueTitle: "Copy issue title",
openLinkedObjective: "Open linked objective",
};
function setup() {
const navigate = vi.fn();
const openInNewTab = vi.fn();
const copyText = vi.fn(async () => undefined);
const openCreateIssue = vi.fn();
const openCreateObjective = vi.fn();
const provider = createMinddyEntityActionsProvider({
labels,
navigate,
openInNewTab,
copyText,
openCreateIssue,
openCreateObjective,
});
return {
provider,
navigate,
openInNewTab,
copyText,
openCreateIssue,
openCreateObjective,
};
}
function item(entityType: string, data?: unknown): PaletteItem {
return {
id: `${entityType}-1`,
title: entityType,
filterCategory: entityType,
entityType,
href: `/projects/project-1/${entityType}-1`,
data,
};
}
function actionIds(entity: PaletteItem): string[] {
return setup().provider.getActions(entity, {} as never).map((action) => action.id);
}
describe("Minddy entity palette actions", () => {
it("keeps generic navigation targets linkable", () => {
expect(actionIds(item("navigation"))).toEqual([
"entity.open-new-tab",
"entity.copy-link",
]);
});
it("covers project creation and every primary project destination", () => {
const ids = actionIds(
item("project", { id: "project-1", key: "MIN", name: "Minddy" })
);
expect(ids).toEqual(
expect.arrayContaining([
"project.new-issue",
"project.new-objective",
"project.copy-key",
"project.objectives",
"project.pages",
"project.triage",
"project.feedback",
"project.settings",
"entity.open-new-tab",
"entity.copy-link",
])
);
expect(ids).toHaveLength(10);
});
it("makes an objective's issues and linked issue creation first-class actions", async () => {
const context = setup();
const objective = item("objective", {
id: "objective-1",
project_id: "project-1",
name: "Ship",
status: "in_progress",
color: null,
});
const actions = context.provider.getActions(objective, {} as never);
expect(actions.map((action) => action.id)).toEqual(
expect.arrayContaining([
"objective.issues",
"objective.new-issue",
"objective.new-objective",
"objective.copy-name",
"objective.list",
"objective.project",
"entity.open-new-tab",
"entity.copy-link",
])
);
await actions.find((action) => action.id === "objective.issues")?.execute?.(
objective,
{} as never
);
expect(context.navigate).toHaveBeenCalledWith(
"/projects/project-1?objective=objective-1"
);
await actions.find((action) => action.id === "objective.new-issue")?.execute?.(
objective,
{} as never
);
expect(context.openCreateIssue).toHaveBeenCalledWith(
"project-1",
"objective-1"
);
});
it("covers page context without exposing unsafe document mutations", () => {
const ids = actionIds(
item("page", {
id: "page-1",
project_id: "project-1",
title: "Runbook",
icon: null,
updated_at: "2026-08-29T00:00:00Z",
})
);
expect(ids).toEqual(
expect.arrayContaining([
"page.new-issue",
"page.copy-title",
"page.index",
"page.project",
"entity.open-new-tab",
"entity.copy-link",
])
);
expect(ids.some((id) => id.includes("delete") || id.includes("trash"))).toBe(false);
});
it("adds objective navigation only to issues that are linked to one", () => {
const baseIssue = {
id: "issue-1",
project_id: "project-1",
number: 42,
title: "Audit palette",
status: "in_progress",
priority: "high",
effort: "m",
assignee_id: null,
updated_at: "2026-08-29T00:00:00Z",
};
const linked = actionIds(
item("issue", { ...baseIssue, objective_id: "objective-1" })
);
const unlinked = actionIds(item("issue", { ...baseIssue, objective_id: null }));
expect(linked).toEqual(
expect.arrayContaining(["issue.open-objective", "issue.objective-issues"])
);
expect(unlinked).not.toContain("issue.open-objective");
expect(unlinked).not.toContain("issue.objective-issues");
});
it("passes stable hrefs through the clipboard and new-tab actions", async () => {
const context = setup();
const navigation = item("navigation");
const actions = context.provider.getActions(navigation, {} as never);
await actions.find((action) => action.id === "entity.copy-link")?.execute?.(
navigation,
{} as never
);
await actions.find((action) => action.id === "entity.open-new-tab")?.execute?.(
navigation,
{} as never
);
expect(context.copyText).toHaveBeenCalledWith(
navigation.href,
labels.linkCopied,
true
);
expect(context.openInNewTab).toHaveBeenCalledWith(navigation.href);
});
it("does not treat a slash-prefixed title as an application link", async () => {
const context = setup();
const page = {
...item("page", {
id: "page-1",
project_id: "project-1",
title: "/Runbook",
icon: null,
updated_at: "2026-08-29T00:00:00Z",
}),
title: "/Runbook",
};
const actions = context.provider.getActions(page, {} as never);
await actions.find((action) => action.id === "page.copy-title")?.execute?.(
page,
{} as never
);
expect(context.copyText).toHaveBeenCalledWith("/Runbook", labels.copied);
});
});