test(FN-4378): cover tracked delete action prompts in task surfaces

Fusion-Task-Id: FN-4378
Fusion-Task-Lineage: d3867ba8-f852-41e3-9db0-584c0ffc23b1
This commit is contained in:
Fusion
2026-05-13 13:56:02 -07:00
committed by gsxdsm
parent c6f18ee48e
commit 95823e71dc
2 changed files with 112 additions and 0 deletions

View File

@@ -1,6 +1,7 @@
import { afterEach, describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent, waitFor, act } from "@testing-library/react";
import { TaskCard, formatElapsedDurationDone, __test_areTaskCardPropsEqual } from "../TaskCard";
import type { ConfirmOptions } from "../../hooks/useConfirm";
import type { Task } from "@fusion/core";
// Mock lucide-react to avoid SVG rendering issues in test env
@@ -38,6 +39,11 @@ vi.mock("../../api", () => ({
fetchAgent: vi.fn(),
}));
const mockConfirm = vi.fn<(options: ConfirmOptions) => Promise<boolean>>();
vi.mock("../../hooks/useConfirm", () => ({
useConfirm: () => ({ confirm: mockConfirm }),
}));
import { uploadAttachment, fetchMission, fetchAgent } from "../../api";
import { loadAllAppCss, loadAllAppCssBaseOnly } from "../../test/cssFixture";
@@ -87,6 +93,50 @@ afterEach(() => {
});
describe("TaskCard", () => {
it("uses githubIssueAction for tracked task delete", async () => {
const onDeleteTask = vi.fn(async () => makeTask());
mockConfirm
.mockResolvedValueOnce(true)
.mockResolvedValueOnce(true);
render(
<TaskCard
task={makeTask({
column: "triage",
githubTracking: {
enabled: true,
issue: { owner: "owner", repo: "repo", number: 42, url: "https://github.com/owner/repo/issues/42", createdAt: "2026-01-01T00:00:00Z" },
},
} as any)}
onOpenDetail={noop}
addToast={noop}
onDeleteTask={onDeleteTask}
/>,
);
await act(async () => {
fireEvent.click(screen.getByLabelText("Delete task"));
});
await waitFor(() => {
expect(onDeleteTask).toHaveBeenCalledWith("FN-001", { githubIssueAction: "close" });
});
});
it("keeps legacy delete options for untracked task", async () => {
const onDeleteTask = vi.fn(async () => makeTask());
mockConfirm.mockResolvedValueOnce(true);
render(<TaskCard task={makeTask({ column: "triage" })} onOpenDetail={noop} addToast={noop} onDeleteTask={onDeleteTask} />);
await act(async () => {
fireEvent.click(screen.getByLabelText("Delete task"));
});
await waitFor(() => {
expect(onDeleteTask).toHaveBeenCalledWith("FN-001");
});
});
it("renders the card ID text", () => {
render(<TaskCard task={makeTask()} onOpenDetail={noop} addToast={noop} />);
expect(screen.getByText("FN-001")).toBeDefined();

View File

@@ -90,6 +90,68 @@ describe("TaskDetailModal", () => {
expect(screen.getByRole("menuitem", { name: "Pause" })).toBeTruthy();
});
it("passes githubIssueAction for tracked tasks", async () => {
const onDeleteTask = vi.fn().mockResolvedValue({} as Task);
mockConfirm
.mockResolvedValueOnce(true)
.mockResolvedValueOnce(true);
render(
<TaskDetailModal
task={makeTask({
githubTracking: {
enabled: true,
issue: {
owner: "owner",
repo: "repo",
number: 42,
url: "https://github.com/owner/repo/issues/42",
createdAt: "2026-01-01T00:00:00.000Z",
},
},
})}
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={onDeleteTask}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
addToast={noop}
/>,
);
fireEvent.click(screen.getByRole("button", { name: /actions/i }));
fireEvent.click(screen.getByRole("menuitem", { name: "Delete" }));
await waitFor(() => {
expect(onDeleteTask).toHaveBeenCalledWith("FN-099", { githubIssueAction: "close" });
});
});
it("keeps legacy delete payload for untracked tasks", async () => {
const onDeleteTask = vi.fn().mockResolvedValue({} as Task);
mockConfirm.mockResolvedValueOnce(true);
render(
<TaskDetailModal
task={makeTask()}
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={onDeleteTask}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
addToast={noop}
/>,
);
fireEvent.click(screen.getByRole("button", { name: /actions/i }));
fireEvent.click(screen.getByRole("menuitem", { name: "Delete" }));
await waitFor(() => {
expect(onDeleteTask).toHaveBeenCalledWith("FN-099");
});
});
it("prompts for dependency-removal confirmation and retries delete with explicit flag", async () => {
const onDeleteTask = vi.fn();
const conflict = new Error("Cannot delete task FN-099: still referenced as a dependency by FN-100, FN-101.") as Error & {