story-kit: merge 236_story_show_test_results_for_a_story_in_expanded_work_item

This commit is contained in:
Dave
2026-02-28 09:38:51 +00:00
parent c434aa3016
commit 894231428e
6 changed files with 609 additions and 37 deletions

View File

@@ -2,6 +2,7 @@ import * as React from "react";
import Markdown from "react-markdown";
import type { AgentEvent, AgentInfo, AgentStatusValue } from "../api/agents";
import { agentsApi, subscribeAgentStream } from "../api/agents";
import type { TestCaseResult, TestResultsResponse } from "../api/client";
import { api } from "../api/client";
const { useEffect, useRef, useState } = React;
@@ -24,11 +25,89 @@ const STATUS_COLORS: Record<AgentStatusValue, string> = {
interface WorkItemDetailPanelProps {
storyId: string;
pipelineVersion: number;
onClose: () => void;
}
function TestCaseRow({ tc }: { tc: TestCaseResult }) {
const isPassing = tc.status === "pass";
return (
<div
data-testid={`test-case-${tc.name}`}
style={{
display: "flex",
flexDirection: "column",
gap: "2px",
padding: "4px 0",
}}
>
<div style={{ display: "flex", alignItems: "center", gap: "6px" }}>
<span
data-testid={`test-status-${tc.name}`}
style={{
fontSize: "0.85em",
color: isPassing ? "#3fb950" : "#f85149",
}}
>
{isPassing ? "PASS" : "FAIL"}
</span>
<span style={{ fontSize: "0.82em", color: "#ccc" }}>{tc.name}</span>
</div>
{tc.details && (
<div
data-testid={`test-details-${tc.name}`}
style={{
fontSize: "0.75em",
color: "#888",
paddingLeft: "22px",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
}}
>
{tc.details}
</div>
)}
</div>
);
}
function TestSection({
title,
tests,
testId,
}: {
title: string;
tests: TestCaseResult[];
testId: string;
}) {
const passCount = tests.filter((t) => t.status === "pass").length;
const failCount = tests.length - passCount;
return (
<div data-testid={testId}>
<div
style={{
fontSize: "0.78em",
fontWeight: 600,
color: "#aaa",
marginBottom: "6px",
}}
>
{title} ({passCount} passed, {failCount} failed)
</div>
{tests.length === 0 ? (
<div style={{ fontSize: "0.75em", color: "#555", fontStyle: "italic" }}>
No tests recorded
</div>
) : (
tests.map((tc) => <TestCaseRow key={tc.name} tc={tc} />)
)}
</div>
);
}
export function WorkItemDetailPanel({
storyId,
pipelineVersion,
onClose,
}: WorkItemDetailPanelProps) {
const [content, setContent] = useState<string | null>(null);
@@ -39,6 +118,9 @@ export function WorkItemDetailPanel({
const [agentInfo, setAgentInfo] = useState<AgentInfo | null>(null);
const [agentLog, setAgentLog] = useState<string[]>([]);
const [agentStatus, setAgentStatus] = useState<AgentStatusValue | null>(null);
const [testResults, setTestResults] = useState<TestResultsResponse | null>(
null,
);
const panelRef = useRef<HTMLDivElement>(null);
const cleanupRef = useRef<(() => void) | null>(null);
@@ -60,6 +142,18 @@ export function WorkItemDetailPanel({
});
}, [storyId]);
// Fetch test results on mount and when pipeline updates arrive.
useEffect(() => {
api
.getTestResults(storyId)
.then((data) => {
setTestResults(data);
})
.catch(() => {
// Silently ignore — test results may not exist yet.
});
}, [storyId, pipelineVersion]);
useEffect(() => {
cleanupRef.current?.();
cleanupRef.current = null;
@@ -126,6 +220,9 @@ export function WorkItemDetailPanel({
}, [onClose]);
const stageLabel = STAGE_LABELS[stage] ?? stage;
const hasTestResults =
testResults &&
(testResults.unit.length > 0 || testResults.integration.length > 0);
return (
<div
@@ -255,6 +352,56 @@ export function WorkItemDetailPanel({
</div>
)}
{/* Test Results section */}
<div
data-testid="test-results-section"
style={{
border: "1px solid #2a2a2a",
borderRadius: "8px",
padding: "10px 12px",
background: "#161616",
}}
>
<div
style={{
fontWeight: 600,
fontSize: "0.8em",
color: "#555",
marginBottom: "8px",
}}
>
Test Results
</div>
{hasTestResults ? (
<div
data-testid="test-results-content"
style={{
display: "flex",
flexDirection: "column",
gap: "12px",
}}
>
<TestSection
title="Unit Tests"
tests={testResults.unit}
testId="test-section-unit"
/>
<TestSection
title="Integration Tests"
tests={testResults.integration}
testId="test-section-integration"
/>
</div>
) : (
<div
data-testid="test-results-empty"
style={{ fontSize: "0.75em", color: "#444" }}
>
No test results recorded
</div>
)}
</div>
<div
style={{
display: "flex",
@@ -336,7 +483,6 @@ export function WorkItemDetailPanel({
{/* Placeholder sections for future content */}
{(
[
{ id: "test-output", label: "Test Output" },
{ id: "coverage", label: "Coverage" },
] as { id: string; label: string }[]
).map(({ id, label }) => (