- Add phase field to workflow step definitions (pre-merge vs post-merge) with persistence and API - Execute pre-merge steps in executor before merge; post-merge steps in merger after successful merge - Pre-merge failures block merge and keep task in in-review; post-merge failures are logged only - Expose phase controls and phase-aware results in the dashboard UI - Add changeset for the published @gsxdsm/fusion package
171 lines
6.3 KiB
TypeScript
171 lines
6.3 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { render, screen } from "@testing-library/react";
|
|
import { WorkflowResultsTab } from "./WorkflowResultsTab";
|
|
import type { WorkflowStepResult } from "@fusion/core";
|
|
|
|
describe("WorkflowResultsTab", () => {
|
|
const mockResults: WorkflowStepResult[] = [
|
|
{
|
|
workflowStepId: "WS-001",
|
|
workflowStepName: "QA Check",
|
|
phase: "pre-merge",
|
|
status: "passed",
|
|
output: "All tests passed successfully.",
|
|
startedAt: "2026-03-31T10:00:00Z",
|
|
completedAt: "2026-03-31T10:02:30Z",
|
|
},
|
|
{
|
|
workflowStepId: "WS-002",
|
|
workflowStepName: "Security Audit",
|
|
phase: "pre-merge",
|
|
status: "failed",
|
|
output: "Found 2 security issues in auth.ts",
|
|
startedAt: "2026-03-31T10:02:35Z",
|
|
completedAt: "2026-03-31T10:03:15Z",
|
|
},
|
|
{
|
|
workflowStepId: "WS-003",
|
|
workflowStepName: "Documentation Review",
|
|
phase: "post-merge",
|
|
status: "skipped",
|
|
output: undefined,
|
|
startedAt: undefined,
|
|
completedAt: undefined,
|
|
},
|
|
{
|
|
workflowStepId: "WS-004",
|
|
workflowStepName: "Performance Check",
|
|
phase: "post-merge",
|
|
status: "pending",
|
|
output: undefined,
|
|
startedAt: "2026-03-31T10:03:20Z",
|
|
completedAt: undefined,
|
|
},
|
|
];
|
|
|
|
it("renders list of workflow step results", () => {
|
|
render(<WorkflowResultsTab taskId="FN-001" results={mockResults} />);
|
|
|
|
expect(screen.getByTestId("workflow-results-list")).toBeInTheDocument();
|
|
expect(screen.getByText("QA Check")).toBeInTheDocument();
|
|
expect(screen.getByText("Security Audit")).toBeInTheDocument();
|
|
expect(screen.getByText("Documentation Review")).toBeInTheDocument();
|
|
expect(screen.getByText("Performance Check")).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders correct status badges for each result", () => {
|
|
render(<WorkflowResultsTab taskId="FN-001" results={mockResults} />);
|
|
|
|
// Passed badge
|
|
const passedBadge = screen.getByTestId("workflow-result-badge-WS-001");
|
|
expect(passedBadge).toHaveTextContent("Passed");
|
|
expect(passedBadge).toHaveStyle({ backgroundColor: "var(--color-success, #3fb950)" });
|
|
|
|
// Failed badge
|
|
const failedBadge = screen.getByTestId("workflow-result-badge-WS-002");
|
|
expect(failedBadge).toHaveTextContent("Failed");
|
|
expect(failedBadge).toHaveStyle({ backgroundColor: "var(--color-error, #f85149)" });
|
|
|
|
// Skipped badge
|
|
const skippedBadge = screen.getByTestId("workflow-result-badge-WS-003");
|
|
expect(skippedBadge).toHaveTextContent("Skipped");
|
|
|
|
// Pending badge
|
|
const pendingBadge = screen.getByTestId("workflow-result-badge-WS-004");
|
|
expect(pendingBadge).toHaveTextContent("Running…");
|
|
expect(pendingBadge).toHaveStyle({ backgroundColor: "var(--todo, #58a6ff)" });
|
|
});
|
|
|
|
it("shows output content for each result", () => {
|
|
render(<WorkflowResultsTab taskId="FN-001" results={mockResults} />);
|
|
|
|
expect(screen.getByTestId("workflow-result-output-WS-001")).toHaveTextContent(
|
|
"All tests passed successfully."
|
|
);
|
|
expect(screen.getByTestId("workflow-result-output-WS-002")).toHaveTextContent(
|
|
"Found 2 security issues in auth.ts"
|
|
);
|
|
});
|
|
|
|
it("handles results without output gracefully", () => {
|
|
render(<WorkflowResultsTab taskId="FN-001" results={mockResults} />);
|
|
|
|
// WS-003 and WS-004 have no output, so output elements should not be rendered
|
|
expect(screen.queryByTestId("workflow-result-output-WS-003")).not.toBeInTheDocument();
|
|
expect(screen.queryByTestId("workflow-result-output-WS-004")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("shows empty state when no results", () => {
|
|
render(<WorkflowResultsTab taskId="FN-001" results={[]} />);
|
|
|
|
expect(screen.getByTestId("workflow-results-empty")).toBeInTheDocument();
|
|
expect(screen.getByText("No workflow steps have run yet.")).toBeInTheDocument();
|
|
});
|
|
|
|
it("shows loading state when loading prop is true", () => {
|
|
render(<WorkflowResultsTab taskId="FN-001" results={[]} loading={true} />);
|
|
|
|
expect(screen.getByTestId("workflow-results-loading")).toBeInTheDocument();
|
|
expect(screen.getByText("Loading workflow results…")).toBeInTheDocument();
|
|
});
|
|
|
|
it("displays execution timestamps when available", () => {
|
|
render(<WorkflowResultsTab taskId="FN-001" results={mockResults} />);
|
|
|
|
// Check that timestamps are displayed for results that have them
|
|
const timestamps = screen.getAllByText(/Started:/);
|
|
expect(timestamps.length).toBeGreaterThanOrEqual(3); // 3 results have startedAt
|
|
});
|
|
|
|
it("displays duration when start and end times are available", () => {
|
|
render(<WorkflowResultsTab taskId="FN-001" results={mockResults} />);
|
|
|
|
// The first result has a 2m 30s duration
|
|
expect(screen.getByText("2m 30s")).toBeInTheDocument();
|
|
});
|
|
|
|
it("handles results with missing timestamps gracefully", () => {
|
|
const resultsWithoutTimestamps: WorkflowStepResult[] = [
|
|
{
|
|
workflowStepId: "WS-005",
|
|
workflowStepName: "Simple Check",
|
|
phase: "pre-merge",
|
|
status: "passed",
|
|
output: "Done",
|
|
},
|
|
];
|
|
|
|
render(<WorkflowResultsTab taskId="FN-001" results={resultsWithoutTimestamps} />);
|
|
|
|
expect(screen.getByText("Simple Check")).toBeInTheDocument();
|
|
// Should not crash without timestamps
|
|
});
|
|
|
|
it("displays phase badges for each result", () => {
|
|
render(<WorkflowResultsTab taskId="FN-001" results={mockResults} />);
|
|
|
|
// Pre-merge results (WS-001, WS-002)
|
|
expect(screen.getByTestId("workflow-result-phase-WS-001")).toHaveTextContent("Pre-merge");
|
|
expect(screen.getByTestId("workflow-result-phase-WS-002")).toHaveTextContent("Pre-merge");
|
|
|
|
// Post-merge results (WS-003, WS-004)
|
|
expect(screen.getByTestId("workflow-result-phase-WS-003")).toHaveTextContent("Post-merge");
|
|
expect(screen.getByTestId("workflow-result-phase-WS-004")).toHaveTextContent("Post-merge");
|
|
});
|
|
|
|
it("defaults to Pre-merge phase badge when phase is undefined", () => {
|
|
const resultsWithoutPhase: WorkflowStepResult[] = [
|
|
{
|
|
workflowStepId: "WS-005",
|
|
workflowStepName: "Legacy Check",
|
|
status: "passed",
|
|
output: "Done",
|
|
},
|
|
];
|
|
|
|
render(<WorkflowResultsTab taskId="FN-001" results={resultsWithoutPhase} />);
|
|
|
|
expect(screen.getByTestId("workflow-result-phase-WS-005")).toHaveTextContent("Pre-merge");
|
|
});
|
|
});
|