271 lines
7.5 KiB
TypeScript
271 lines
7.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
appendMissingOrderKeys,
|
|
applyStoredOrdering,
|
|
buildSidebarProjectsFromWorkspaces,
|
|
} from "./use-sidebar-workspaces-list";
|
|
import type { WorkspaceDescriptor } from "@/stores/session-store";
|
|
|
|
interface OrderedItem {
|
|
key: string;
|
|
}
|
|
|
|
function item(key: string): OrderedItem {
|
|
return { key };
|
|
}
|
|
|
|
function workspace(
|
|
input: Pick<WorkspaceDescriptor, "id" | "projectId" | "name" | "status" | "activityAt"> &
|
|
Partial<
|
|
Pick<
|
|
WorkspaceDescriptor,
|
|
"projectDisplayName" | "projectRootPath" | "projectKind" | "workspaceKind"
|
|
>
|
|
>,
|
|
): WorkspaceDescriptor {
|
|
return {
|
|
id: input.id,
|
|
projectId: input.projectId,
|
|
projectDisplayName: input.projectDisplayName ?? input.projectId,
|
|
projectRootPath: input.projectRootPath ?? input.id,
|
|
projectKind: input.projectKind ?? "git",
|
|
workspaceKind: input.workspaceKind ?? "local_checkout",
|
|
name: input.name,
|
|
status: input.status,
|
|
activityAt: input.activityAt,
|
|
diffStat: null,
|
|
};
|
|
}
|
|
|
|
describe("applyStoredOrdering", () => {
|
|
it("keeps unknown items on the baseline while applying stored order", () => {
|
|
const result = applyStoredOrdering({
|
|
items: [item("new"), item("a"), item("b")],
|
|
storedOrder: ["b", "a"],
|
|
getKey: (entry) => entry.key,
|
|
});
|
|
|
|
expect(result.map((entry) => entry.key)).toEqual(["new", "b", "a"]);
|
|
});
|
|
|
|
it("ignores stale and duplicate stored keys", () => {
|
|
const result = applyStoredOrdering({
|
|
items: [item("x"), item("y")],
|
|
storedOrder: ["missing", "y", "y", "x"],
|
|
getKey: (entry) => entry.key,
|
|
});
|
|
|
|
expect(result.map((entry) => entry.key)).toEqual(["y", "x"]);
|
|
});
|
|
|
|
it("returns baseline when there is no persisted order", () => {
|
|
const baseline = [item("first"), item("second")];
|
|
const result = applyStoredOrdering({
|
|
items: baseline,
|
|
storedOrder: [],
|
|
getKey: (entry) => entry.key,
|
|
});
|
|
|
|
expect(result).toBe(baseline);
|
|
});
|
|
});
|
|
|
|
describe("appendMissingOrderKeys", () => {
|
|
it("appends unseen keys while preserving existing order", () => {
|
|
const result = appendMissingOrderKeys({
|
|
currentOrder: ["project-b", "project-a"],
|
|
visibleKeys: ["project-a", "project-b", "project-c"],
|
|
});
|
|
|
|
expect(result).toEqual(["project-b", "project-a", "project-c"]);
|
|
});
|
|
|
|
it("returns the same array when there are no unseen keys", () => {
|
|
const currentOrder = ["project-a", "project-b"];
|
|
|
|
const result = appendMissingOrderKeys({
|
|
currentOrder,
|
|
visibleKeys: ["project-b", "project-a"],
|
|
});
|
|
|
|
expect(result).toBe(currentOrder);
|
|
});
|
|
});
|
|
|
|
describe("buildSidebarProjectsFromWorkspaces", () => {
|
|
it("uses workspace descriptor name and status directly", () => {
|
|
const workspaces: WorkspaceDescriptor[] = [
|
|
workspace({
|
|
id: "/repo/main",
|
|
projectId: "project-1",
|
|
name: "feat/hard-cut",
|
|
status: "failed",
|
|
activityAt: new Date("2026-01-01T00:00:00.000Z"),
|
|
}),
|
|
];
|
|
|
|
const projects = buildSidebarProjectsFromWorkspaces({
|
|
serverId: "srv",
|
|
workspaces,
|
|
projectOrder: [],
|
|
workspaceOrderByScope: {},
|
|
});
|
|
|
|
expect(projects).toHaveLength(1);
|
|
expect(projects[0]?.statusBucket).toBe("failed");
|
|
expect(projects[0]?.workspaces[0]?.name).toBe("feat/hard-cut");
|
|
expect(projects[0]?.workspaces[0]?.statusBucket).toBe("failed");
|
|
});
|
|
|
|
it("preserves stored project order even when activity changes", () => {
|
|
const initialWorkspaces: WorkspaceDescriptor[] = [
|
|
workspace({
|
|
id: "/repo/b",
|
|
projectId: "project-b",
|
|
name: "feat/b",
|
|
status: "running",
|
|
activityAt: new Date("2026-01-02T00:00:00.000Z"),
|
|
}),
|
|
workspace({
|
|
id: "/repo/a",
|
|
projectId: "project-a",
|
|
name: "feat/a",
|
|
status: "running",
|
|
activityAt: new Date("2026-01-01T00:00:00.000Z"),
|
|
}),
|
|
];
|
|
|
|
const seededOrder = appendMissingOrderKeys({
|
|
currentOrder: [],
|
|
visibleKeys: buildSidebarProjectsFromWorkspaces({
|
|
serverId: "srv",
|
|
workspaces: initialWorkspaces,
|
|
projectOrder: [],
|
|
workspaceOrderByScope: {},
|
|
}).map((project) => project.projectKey),
|
|
});
|
|
|
|
const updatedProjects = buildSidebarProjectsFromWorkspaces({
|
|
serverId: "srv",
|
|
workspaces: [
|
|
workspace({
|
|
id: "/repo/b",
|
|
projectId: "project-b",
|
|
name: "feat/b",
|
|
status: "running",
|
|
activityAt: new Date("2026-01-02T00:00:00.000Z"),
|
|
}),
|
|
workspace({
|
|
id: "/repo/a",
|
|
projectId: "project-a",
|
|
name: "feat/a",
|
|
status: "running",
|
|
activityAt: new Date("2026-01-03T00:00:00.000Z"),
|
|
}),
|
|
],
|
|
projectOrder: seededOrder,
|
|
workspaceOrderByScope: {},
|
|
});
|
|
|
|
expect(updatedProjects.map((project) => project.projectKey)).toEqual([
|
|
"project-b",
|
|
"project-a",
|
|
]);
|
|
});
|
|
|
|
it("appends new projects after the stored project order", () => {
|
|
const projects = buildSidebarProjectsFromWorkspaces({
|
|
serverId: "srv",
|
|
workspaces: [
|
|
workspace({
|
|
id: "/repo/c",
|
|
projectId: "project-c",
|
|
name: "feat/c",
|
|
status: "running",
|
|
activityAt: new Date("2026-01-04T00:00:00.000Z"),
|
|
}),
|
|
workspace({
|
|
id: "/repo/b",
|
|
projectId: "project-b",
|
|
name: "feat/b",
|
|
status: "running",
|
|
activityAt: new Date("2026-01-02T00:00:00.000Z"),
|
|
}),
|
|
workspace({
|
|
id: "/repo/a",
|
|
projectId: "project-a",
|
|
name: "feat/a",
|
|
status: "running",
|
|
activityAt: new Date("2026-01-01T00:00:00.000Z"),
|
|
}),
|
|
],
|
|
projectOrder: ["project-b", "project-a", "project-c"],
|
|
workspaceOrderByScope: {},
|
|
});
|
|
|
|
expect(projects.map((project) => project.projectKey)).toEqual([
|
|
"project-b",
|
|
"project-a",
|
|
"project-c",
|
|
]);
|
|
});
|
|
|
|
it("preserves stored workspace order when workspace activity changes", () => {
|
|
const initialProjects = buildSidebarProjectsFromWorkspaces({
|
|
serverId: "srv",
|
|
workspaces: [
|
|
workspace({
|
|
id: "/repo/main",
|
|
projectId: "project-1",
|
|
name: "main",
|
|
status: "running",
|
|
activityAt: new Date("2026-01-02T00:00:00.000Z"),
|
|
}),
|
|
workspace({
|
|
id: "/repo/feature",
|
|
projectId: "project-1",
|
|
name: "feature",
|
|
status: "running",
|
|
activityAt: new Date("2026-01-01T00:00:00.000Z"),
|
|
}),
|
|
],
|
|
projectOrder: ["project-1"],
|
|
workspaceOrderByScope: {},
|
|
});
|
|
|
|
const seededWorkspaceOrder = appendMissingOrderKeys({
|
|
currentOrder: [],
|
|
visibleKeys: initialProjects[0]?.workspaces.map((workspace) => workspace.workspaceKey) ?? [],
|
|
});
|
|
|
|
const projects = buildSidebarProjectsFromWorkspaces({
|
|
serverId: "srv",
|
|
workspaces: [
|
|
workspace({
|
|
id: "/repo/main",
|
|
projectId: "project-1",
|
|
name: "main",
|
|
status: "running",
|
|
activityAt: new Date("2026-01-02T00:00:00.000Z"),
|
|
}),
|
|
workspace({
|
|
id: "/repo/feature",
|
|
projectId: "project-1",
|
|
name: "feature",
|
|
status: "running",
|
|
activityAt: new Date("2026-01-03T00:00:00.000Z"),
|
|
}),
|
|
],
|
|
projectOrder: ["project-1"],
|
|
workspaceOrderByScope: {
|
|
"srv::project-1": seededWorkspaceOrder,
|
|
},
|
|
});
|
|
|
|
expect(projects[0]?.workspaces.map((workspace) => workspace.workspaceId)).toEqual([
|
|
"/repo/main",
|
|
"/repo/feature",
|
|
]);
|
|
});
|
|
});
|