Add dashboard and API support for viewing shared branch groups and triggering group merge actions. - add branch-group API routes and register them in integrated routers - extend dashboard legacy API client with branch-group fetch and merge-control actions - add BranchGroupCard UI + styles and surface it in task/detail/subtask views - add dashboard/API test coverage for branch-group routes and UI integration points - document branch-group behavior and add a changeset for @runfusion/fusion Files changed: .changeset/fn-5822-branch-group-dashboard.md | 5 + docs/dashboard-guide.md | 31 ++++++ packages/dashboard/app/api/legacy.ts | 54 +++++++++ .../dashboard/app/components/BranchGroupCard.css | 83 ++++++++++++++ .../dashboard/app/components/BranchGroupCard.tsx | 123 +++++++++++++++++++++ .../app/components/SubtaskBreakdownModal.tsx | 3 + packages/dashboard/app/components/TaskCard.tsx | 19 ++++ .../dashboard/app/components/TaskDetailModal.tsx | 4 + .../components/__tests__/BranchGroupCard.test.tsx | 96 ++++++++++++++++ .../__tests__/SubtaskBreakdownModal.test.tsx | 1 + .../app/components/__tests__/TaskCard.test.tsx | 16 ++++ .../components/__tests__/TaskDetailModal.test.tsx | 22 ++++ .../src/__tests__/routes-branch-groups.test.ts | 119 ++++++++++++++++++++ .../src/routes/register-branch-groups-routes.ts | 118 ++++++++++++++++++++ .../src/routes/register-integrated-routers.ts | 13 +++ packages/dashboard/vitest.config.ts | 4 +- 16 files changed, 709 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-5822 Fusion-Task-Lineage: 199c25b8-f6ec-43b4-8fab-509f23fb5ac7
97 lines
3.9 KiB
TypeScript
97 lines
3.9 KiB
TypeScript
import React from "react";
|
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
import { BranchGroupCard } from "../BranchGroupCard";
|
|
|
|
const apiGetBranchGroup = vi.fn();
|
|
const apiPromoteBranchGroup = vi.fn();
|
|
|
|
vi.mock("../../api", () => ({
|
|
apiGetBranchGroup: (...args: unknown[]) => apiGetBranchGroup(...args),
|
|
apiPromoteBranchGroup: (...args: unknown[]) => apiPromoteBranchGroup(...args),
|
|
}));
|
|
|
|
vi.mock("../../sse-bus", () => ({
|
|
subscribeSse: () => () => {},
|
|
}));
|
|
|
|
vi.mock("lucide-react", () => ({
|
|
CheckCircle2: () => null,
|
|
CircleDashed: () => null,
|
|
ExternalLink: () => null,
|
|
GitBranch: () => null,
|
|
GitPullRequest: () => null,
|
|
Loader2: () => null,
|
|
}));
|
|
|
|
function makeGroup(overrides: Record<string, unknown> = {}) {
|
|
return {
|
|
id: "BG-1",
|
|
sourceType: "planning",
|
|
sourceId: "PS-1",
|
|
branchName: "feature/shared",
|
|
autoMerge: false,
|
|
prState: "none",
|
|
status: "open",
|
|
createdAt: Date.now(),
|
|
updatedAt: Date.now(),
|
|
members: [
|
|
{ taskId: "FN-1", title: "one", column: "done", landed: true },
|
|
{ taskId: "FN-2", title: "two", column: "in-review", landed: false },
|
|
],
|
|
completion: { landed: 1, total: 2, complete: false },
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("BranchGroupCard", () => {
|
|
beforeEach(() => {
|
|
apiGetBranchGroup.mockReset();
|
|
apiPromoteBranchGroup.mockReset();
|
|
});
|
|
|
|
it("hides promote control while incomplete", async () => {
|
|
apiGetBranchGroup.mockResolvedValue({ group: makeGroup() });
|
|
render(<BranchGroupCard groupId="BG-1" />);
|
|
await screen.findByText("1 of 2 members finished");
|
|
expect(screen.queryByRole("button", { name: /open pr|merge group into main/i })).toBeNull();
|
|
});
|
|
|
|
it("shows promote and calls API when complete + autoMerge off", async () => {
|
|
apiGetBranchGroup
|
|
.mockResolvedValueOnce({ group: makeGroup({ completion: { landed: 2, total: 2, complete: true }, members: [{ taskId: "FN-1", title: "one", column: "done", landed: true }, { taskId: "FN-2", title: "two", column: "done", landed: true }] }) })
|
|
.mockResolvedValueOnce({ group: makeGroup({ completion: { landed: 2, total: 2, complete: true }, members: [{ taskId: "FN-1", title: "one", column: "done", landed: true }, { taskId: "FN-2", title: "two", column: "done", landed: true }], prState: "open", prNumber: 22, prUrl: "https://example/pr/22" }) });
|
|
apiPromoteBranchGroup.mockResolvedValue({ groupId: "BG-1", prState: "open" });
|
|
|
|
render(<BranchGroupCard groupId="BG-1" />);
|
|
const button = await screen.findByRole("button", { name: /open pr/i });
|
|
fireEvent.click(button);
|
|
|
|
await waitFor(() => {
|
|
expect(apiPromoteBranchGroup).toHaveBeenCalledWith("BG-1", undefined);
|
|
});
|
|
});
|
|
|
|
it("shows auto-merge badge when complete and autoMerge is on", async () => {
|
|
apiGetBranchGroup.mockResolvedValue({
|
|
group: makeGroup({
|
|
autoMerge: true,
|
|
completion: { landed: 2, total: 2, complete: true },
|
|
members: [{ taskId: "FN-1", title: "one", column: "done", landed: true }, { taskId: "FN-2", title: "two", column: "done", landed: true }],
|
|
}),
|
|
});
|
|
|
|
render(<BranchGroupCard groupId="BG-1" />);
|
|
expect(await screen.findByText("Auto-merge enabled")).toBeInTheDocument();
|
|
expect(screen.queryByRole("button", { name: /open pr|merge group into main/i })).toBeNull();
|
|
});
|
|
|
|
it("renders tracked group PR link when present", async () => {
|
|
apiGetBranchGroup.mockResolvedValue({
|
|
group: makeGroup({ completion: { landed: 2, total: 2, complete: true }, members: [{ taskId: "FN-1", title: "one", column: "done", landed: true }, { taskId: "FN-2", title: "two", column: "done", landed: true }], prState: "open", prNumber: 9, prUrl: "https://example/pr/9" }),
|
|
});
|
|
render(<BranchGroupCard groupId="BG-1" />);
|
|
expect(await screen.findByRole("link", { name: /pr #9/i })).toBeInTheDocument();
|
|
});
|
|
});
|