feat: add workflow console lifecycle explorer

This commit is contained in:
lda
2026-07-02 21:43:04 +07:00 Verified
parent 2cae5b59eb
commit 3cdcf6ff3b
31 changed files with 4457 additions and 12 deletions
@@ -0,0 +1,183 @@
import { describe, it, expect } from "vitest";
import {
lifecycleReducer,
initialLifecycleState,
type LifecycleState,
type LifecycleAction,
} from "./state.js";
describe("lifecycleReducer", () => {
it("selectArtifact clears deployment and run selections", () => {
const state: LifecycleState = {
...initialLifecycleState,
selectedArtifactId: "old@1",
selectedDeploymentId: "old.default",
selectedRunId: "run_1",
deploymentDetail: { id: "old.default" } as LifecycleState["deploymentDetail"],
runDetail: { runId: "run_1" } as LifecycleState["runDetail"],
trace: { frames: [], traceStart: 0, traceLimit: 50, traceTruncated: false } as LifecycleState["trace"],
};
const result = lifecycleReducer(state, {
type: "selectArtifact",
artifactId: "report@1",
});
expect(result.selectedArtifactId).toBe("report@1");
expect(result.selectedDeploymentId).toBeNull();
expect(result.selectedRunId).toBeNull();
expect(result.deploymentDetail).toBeNull();
expect(result.runDetail).toBeNull();
expect(result.trace).toBeNull();
});
it("selectDeployment clears run selection", () => {
const state: LifecycleState = {
...initialLifecycleState,
selectedArtifactId: "report@1",
selectedDeploymentId: "old.default",
selectedRunId: "run_1",
runDetail: { runId: "run_1" } as LifecycleState["runDetail"],
trace: { frames: [], traceStart: 0, traceLimit: 50, traceTruncated: false } as LifecycleState["trace"],
};
const result = lifecycleReducer(state, {
type: "selectDeployment",
deploymentId: "report.default",
});
expect(result.selectedDeploymentId).toBe("report.default");
expect(result.selectedRunId).toBeNull();
expect(result.runDetail).toBeNull();
expect(result.trace).toBeNull();
});
it("targetChanged resets to initial state", () => {
const state: LifecycleState = {
...initialLifecycleState,
selectedArtifactId: "report@1",
artifactList: { phase: "loaded", value: { items: [], total: 0, nextCursor: null } },
};
const result = lifecycleReducer(state, { type: "targetChanged" });
expect(result).toEqual(initialLifecycleState);
});
it("handles loading states", () => {
const state = initialLifecycleState;
const result = lifecycleReducer(state, {
type: "setArtifactListPhase",
phase: "loading",
});
expect(result.artifactList.phase).toBe("loading");
});
it("handles loaded states", () => {
const state = initialLifecycleState;
const result = lifecycleReducer(state, {
type: "setArtifactListPhase",
phase: "loaded",
value: { items: [], total: 0, nextCursor: null },
});
expect(result.artifactList.phase).toBe("loaded");
});
it("handles error states", () => {
const state = initialLifecycleState;
const result = lifecycleReducer(state, {
type: "setArtifactListPhase",
phase: "error",
message: "failed to load",
});
expect(result.artifactList.phase).toBe("error");
});
it("appendArtifactList merges new items with existing", () => {
const state: LifecycleState = {
...initialLifecycleState,
artifactList: {
phase: "loaded",
value: {
items: [
{ key: "report@1", artifactId: "report", version: 1, kind: "workflow", displayName: "Report", description: null, outcomes: ["ok"], requiredSources: [], diagnosticCount: 0 },
],
total: 2,
nextCursor: "cursor_1",
},
},
};
const result = lifecycleReducer(state, {
type: "appendArtifactList",
value: {
items: [
{ key: "summary@1", artifactId: "summary", version: 1, kind: "workflow", displayName: "Summary", description: null, outcomes: ["ok"], requiredSources: [], diagnosticCount: 0 },
],
total: 2,
nextCursor: null,
},
});
if (result.artifactList.phase !== "loaded") throw new Error("expected loaded");
expect(result.artifactList.value.items).toHaveLength(2);
expect(result.artifactList.value.items[0]!.artifactId).toBe("report");
expect(result.artifactList.value.items[1]!.artifactId).toBe("summary");
expect(result.artifactList.value.nextCursor).toBeNull();
});
it("appendRunList merges new items with existing", () => {
const state: LifecycleState = {
...initialLifecycleState,
runList: {
phase: "loaded",
value: {
items: [
{ runId: "run_1", deploymentId: "report.default", artifactId: "report", artifactVersion: 1, status: "interrupted", resumeReadiness: "ready", diagnosticCount: 0 },
],
total: 2,
nextCursor: "cursor_1",
},
},
};
const result = lifecycleReducer(state, {
type: "appendRunList",
value: {
items: [
{ runId: "run_2", deploymentId: "report.default", artifactId: "report", artifactVersion: 1, status: "completed", resumeReadiness: "none", diagnosticCount: 0 },
],
total: 2,
nextCursor: null,
},
});
if (result.runList.phase !== "loaded") throw new Error("expected loaded");
expect(result.runList.value.items).toHaveLength(2);
expect(result.runList.value.items[0]!.runId).toBe("run_1");
expect(result.runList.value.items[1]!.runId).toBe("run_2");
expect(result.runList.value.nextCursor).toBeNull();
});
it("appendArtifactList initializes from idle state", () => {
const result = lifecycleReducer(initialLifecycleState, {
type: "appendArtifactList",
value: {
items: [
{ key: "report@1", artifactId: "report", version: 1, kind: "workflow", displayName: "Report", description: null, outcomes: ["ok"], requiredSources: [], diagnosticCount: 0 },
],
total: 1,
nextCursor: null,
},
});
if (result.artifactList.phase !== "loaded") throw new Error("expected loaded");
expect(result.artifactList.value.items).toHaveLength(1);
});
});