149 lines
4.2 KiB
TypeScript
149 lines
4.2 KiB
TypeScript
|
|
import { render } from "@testing-library/react";
|
||
|
|
import * as React from "react";
|
||
|
|
import { describe, expect, it } from "vitest";
|
||
|
|
import type { PipelineState } from "../api/client";
|
||
|
|
import { LozengeFlyProvider } from "./LozengeFlyContext";
|
||
|
|
import { StagePanel } from "./StagePanel";
|
||
|
|
|
||
|
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
function makePipeline(overrides: Partial<PipelineState> = {}): PipelineState {
|
||
|
|
return {
|
||
|
|
backlog: [],
|
||
|
|
current: [],
|
||
|
|
qa: [],
|
||
|
|
merge: [],
|
||
|
|
done: [],
|
||
|
|
...overrides,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function Wrapper({
|
||
|
|
pipeline,
|
||
|
|
children,
|
||
|
|
}: {
|
||
|
|
pipeline: PipelineState;
|
||
|
|
children: React.ReactNode;
|
||
|
|
}) {
|
||
|
|
return (
|
||
|
|
<LozengeFlyProvider pipeline={pipeline}>{children}</LozengeFlyProvider>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ─── Agent lozenge fixed intrinsic width ──────────────────────────────────────
|
||
|
|
|
||
|
|
describe("AgentLozenge fixed intrinsic width", () => {
|
||
|
|
it("has align-self: flex-start so it never stretches inside a flex column", () => {
|
||
|
|
const items = [
|
||
|
|
{
|
||
|
|
story_id: "74_width_test",
|
||
|
|
name: "Width Test",
|
||
|
|
error: null,
|
||
|
|
merge_failure: null,
|
||
|
|
agent: { agent_name: "coder-1", model: "sonnet", status: "running" },
|
||
|
|
review_hold: null,
|
||
|
|
qa: null,
|
||
|
|
depends_on: null,
|
||
|
|
},
|
||
|
|
];
|
||
|
|
const pipeline = makePipeline({ current: items });
|
||
|
|
const { container } = render(
|
||
|
|
<Wrapper pipeline={pipeline}>
|
||
|
|
<StagePanel title="Current" items={items} />
|
||
|
|
</Wrapper>,
|
||
|
|
);
|
||
|
|
|
||
|
|
const lozenge = container.querySelector(
|
||
|
|
'[data-testid="slot-lozenge-74_width_test"]',
|
||
|
|
) as HTMLElement;
|
||
|
|
expect(lozenge).toBeInTheDocument();
|
||
|
|
expect(lozenge.style.alignSelf).toBe("flex-start");
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// ─── Idle vs active visual distinction ────────────────────────────────────────
|
||
|
|
|
||
|
|
describe("AgentLozenge idle vs active appearance", () => {
|
||
|
|
it("running agent lozenge uses the green active color", () => {
|
||
|
|
const items = [
|
||
|
|
{
|
||
|
|
story_id: "74_running_color",
|
||
|
|
name: "Running",
|
||
|
|
error: null,
|
||
|
|
merge_failure: null,
|
||
|
|
agent: { agent_name: "coder-1", model: null, status: "running" },
|
||
|
|
review_hold: null,
|
||
|
|
qa: null,
|
||
|
|
depends_on: null,
|
||
|
|
},
|
||
|
|
];
|
||
|
|
const { container } = render(
|
||
|
|
<Wrapper pipeline={makePipeline({ current: items })}>
|
||
|
|
<StagePanel title="Current" items={items} />
|
||
|
|
</Wrapper>,
|
||
|
|
);
|
||
|
|
|
||
|
|
const lozenge = container.querySelector(
|
||
|
|
'[data-testid="slot-lozenge-74_running_color"]',
|
||
|
|
) as HTMLElement;
|
||
|
|
expect(lozenge).toBeInTheDocument();
|
||
|
|
// Green: rgb(63, 185, 80) = #3fb950
|
||
|
|
expect(lozenge.style.color).toBe("rgb(63, 185, 80)");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("pending agent lozenge uses the yellow pending color", () => {
|
||
|
|
const items = [
|
||
|
|
{
|
||
|
|
story_id: "74_pending_color",
|
||
|
|
name: "Pending",
|
||
|
|
error: null,
|
||
|
|
merge_failure: null,
|
||
|
|
agent: { agent_name: "coder-1", model: null, status: "pending" },
|
||
|
|
review_hold: null,
|
||
|
|
qa: null,
|
||
|
|
depends_on: null,
|
||
|
|
},
|
||
|
|
];
|
||
|
|
const { container } = render(
|
||
|
|
<Wrapper pipeline={makePipeline({ current: items })}>
|
||
|
|
<StagePanel title="Current" items={items} />
|
||
|
|
</Wrapper>,
|
||
|
|
);
|
||
|
|
|
||
|
|
const lozenge = container.querySelector(
|
||
|
|
'[data-testid="slot-lozenge-74_pending_color"]',
|
||
|
|
) as HTMLElement;
|
||
|
|
expect(lozenge).toBeInTheDocument();
|
||
|
|
// Yellow: rgb(227, 179, 65) = #e3b341
|
||
|
|
expect(lozenge.style.color).toBe("rgb(227, 179, 65)");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("running lozenge has a pulsing dot child element", () => {
|
||
|
|
const items = [
|
||
|
|
{
|
||
|
|
story_id: "74_pulse_dot",
|
||
|
|
name: "Pulse",
|
||
|
|
error: null,
|
||
|
|
merge_failure: null,
|
||
|
|
agent: { agent_name: "coder-1", model: null, status: "running" },
|
||
|
|
review_hold: null,
|
||
|
|
qa: null,
|
||
|
|
depends_on: null,
|
||
|
|
},
|
||
|
|
];
|
||
|
|
const { container } = render(
|
||
|
|
<Wrapper pipeline={makePipeline({ current: items })}>
|
||
|
|
<StagePanel title="Current" items={items} />
|
||
|
|
</Wrapper>,
|
||
|
|
);
|
||
|
|
|
||
|
|
const lozenge = container.querySelector(
|
||
|
|
'[data-testid="slot-lozenge-74_pulse_dot"]',
|
||
|
|
) as HTMLElement;
|
||
|
|
// The pulse dot is a child span with animation: pulse
|
||
|
|
const dot = lozenge.querySelector("span");
|
||
|
|
expect(dot).not.toBeNull();
|
||
|
|
expect(dot?.style.animation).toContain("pulse");
|
||
|
|
});
|
||
|
|
});
|