Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
259 lines
8.2 KiB
TypeScript
259 lines
8.2 KiB
TypeScript
import "@testing-library/jest-dom/vitest";
|
|
import { render, screen } from "@testing-library/react";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import {
|
|
TuttiModePlanPanel,
|
|
type TuttiModePlanPanelLabels
|
|
} from "./TuttiModePlanPanel";
|
|
import type { TuttiModePlanPanelViewModel } from "./tuttiModePlanPanelProjection";
|
|
import type { TuttiModePlanAssignmentCatalog } from "./useTuttiModePlanPanels";
|
|
|
|
const labels: TuttiModePlanPanelLabels = {
|
|
tasks: "Tasks",
|
|
priority: "Priority",
|
|
priorityHigh: "High",
|
|
priorityMedium: "Medium",
|
|
priorityLow: "Low",
|
|
agentTarget: "Agent",
|
|
model: "Model",
|
|
permissionMode: "Permission mode",
|
|
reasoningEffort: "Reasoning effort",
|
|
parallelizable: "Parallel",
|
|
autoAccept: "Auto-accept",
|
|
notSpecified: "Not specified",
|
|
assignmentOptionsLoading: "Loading options..."
|
|
};
|
|
|
|
const panel: TuttiModePlanPanelViewModel = {
|
|
id: "workflow-1:checkpoint-1",
|
|
workflowId: "workflow-1",
|
|
workspaceId: "workspace-1",
|
|
sourceSessionId: "session-1",
|
|
sourceTurnId: "turn-1",
|
|
sourceToolCallId: "tool-call-1",
|
|
reviewKind: "task_review",
|
|
state: "pending",
|
|
actionable: true,
|
|
title: "Ship the durable workflow",
|
|
topicId: "topic-1",
|
|
markdownBody: "## Goal\n\nShip safely.",
|
|
revision: {
|
|
id: "revision-1",
|
|
sequence: 1,
|
|
schemaVersion: "tutti-mode-plan/v1",
|
|
documentPath: `tutti-mode-plans/workflow-1/revisions/${"a".repeat(64)}.md`,
|
|
sha256: "a".repeat(64),
|
|
producedByTurnId: "turn-1",
|
|
createdAtUnixMs: 100
|
|
},
|
|
checkpoint: {
|
|
id: "checkpoint-1",
|
|
status: "pending",
|
|
decidedBy: null,
|
|
decisionReason: null,
|
|
decidedAtUnixMs: null,
|
|
createdAtUnixMs: 110,
|
|
updatedAtUnixMs: 110
|
|
},
|
|
execution: {
|
|
mode: "sequential",
|
|
reasoningIntensity: 70,
|
|
orchestrationIntensity: 60
|
|
},
|
|
budget: {
|
|
mode: "fixed",
|
|
tokenLimit: 12_000,
|
|
quotaWaterlinePercent: 15
|
|
},
|
|
tasks: [
|
|
{
|
|
ordinal: 1,
|
|
id: "implement",
|
|
title: "Implement",
|
|
content: "Build the workflow",
|
|
priority: "high",
|
|
agentTargetId: "codex-agent",
|
|
modelPlanId: "model-plan-pro",
|
|
model: "gpt-5.6-sol",
|
|
permissionModeId: "acceptEdits",
|
|
reasoningEffort: "high",
|
|
executionDirectory: "/workspace/implement",
|
|
dependsOn: ["foundation", "contract-tests"],
|
|
parallelizable: true,
|
|
autoAccept: true
|
|
}
|
|
]
|
|
};
|
|
|
|
function assignmentCatalog(
|
|
overrides: Partial<TuttiModePlanAssignmentCatalog> = {}
|
|
): TuttiModePlanAssignmentCatalog {
|
|
return {
|
|
agents: [
|
|
{ agentTargetId: "codex-agent", label: "Codex" },
|
|
{ agentTargetId: "claude-agent", label: "Claude Code" }
|
|
],
|
|
optionsByAgentId: {
|
|
"codex-agent": {
|
|
models: [
|
|
{ value: "gpt-5.6-sol", label: "GPT-5.6 Sol" },
|
|
{
|
|
value: "composer-2.5[fast=true]",
|
|
label: "composer-2.5"
|
|
}
|
|
],
|
|
modelPlans: [
|
|
{
|
|
modelPlanId: "model-plan-pro",
|
|
label: "Pro plan",
|
|
models: [{ value: "gpt-5.6-sol", label: "GPT-5.6 Sol" }]
|
|
}
|
|
],
|
|
permissionModes: [{ id: "acceptEdits", label: "Accept edits" }],
|
|
reasoningEfforts: [
|
|
{ value: "low", label: "Low" },
|
|
{ value: "high", label: "High" }
|
|
]
|
|
}
|
|
},
|
|
loadAgentOptions: vi.fn(),
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
describe("TuttiModePlanPanel", () => {
|
|
it("renders the plan without duplicating workflow phase badges", () => {
|
|
render(
|
|
<TuttiModePlanPanel labels={labels} panel={panel} submitting={false} />
|
|
);
|
|
|
|
expect(screen.getByText("Ship the durable workflow")).toBeInTheDocument();
|
|
expect(screen.queryByText("Tutti mode plan")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("Plan review")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("Needs review")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("hides plan internals: execution/budget cards, task metadata, decisions", () => {
|
|
render(
|
|
<TuttiModePlanPanel labels={labels} panel={panel} submitting={false} />
|
|
);
|
|
|
|
// Execution / budget tuning happens by re-planning, not on the card.
|
|
expect(screen.queryByText("Execution")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("Budget")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("Sequential")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("Quota waterline")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("15%")).not.toBeInTheDocument();
|
|
// Task ID / execution directory / dependencies stay out of the card.
|
|
expect(screen.queryByText("/workspace/implement")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("foundation")).not.toBeInTheDocument();
|
|
// Decisions live in the composer now — no footer buttons.
|
|
expect(
|
|
screen.queryByRole("button", { name: "Accept" })
|
|
).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("shows specified assignments read-only without an assignment catalog", () => {
|
|
render(
|
|
<TuttiModePlanPanel labels={labels} panel={panel} submitting={false} />
|
|
);
|
|
|
|
expect(screen.getByText("codex-agent")).toBeInTheDocument();
|
|
expect(screen.getByText("gpt-5.6-sol")).toBeInTheDocument();
|
|
expect(screen.getByText("acceptEdits")).toBeInTheDocument();
|
|
expect(screen.getByText("high")).toBeInTheDocument();
|
|
expect(screen.getByText("Parallel")).toBeInTheDocument();
|
|
// Model plans never surface on tasks — plans enter via agent config.
|
|
expect(screen.queryByText("model-plan-pro")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("renders per-task assignment selectors without a model plan select", () => {
|
|
render(
|
|
<TuttiModePlanPanel
|
|
assignmentCatalog={assignmentCatalog()}
|
|
assignmentDrafts={{}}
|
|
labels={labels}
|
|
panel={panel}
|
|
submitting={false}
|
|
onAssignmentDraftChange={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
expect(
|
|
screen.getByTestId("tutti-plan-task-assignment-implement")
|
|
).toBeInTheDocument();
|
|
expect(screen.getByLabelText("Agent")).toBeInTheDocument();
|
|
expect(screen.getByLabelText("Model")).toBeInTheDocument();
|
|
expect(screen.getByLabelText("Permission mode")).toBeInTheDocument();
|
|
expect(screen.getByLabelText("Reasoning effort")).toBeInTheDocument();
|
|
expect(screen.queryByLabelText("Model plan")).not.toBeInTheDocument();
|
|
// The parallel opt-in renders as a checked switch seeded from the plan.
|
|
const parallelSwitch = screen.getByTestId(
|
|
"tutti-plan-task-parallel-toggle-implement"
|
|
);
|
|
expect(parallelSwitch).toHaveAttribute("role", "switch");
|
|
expect(parallelSwitch).toHaveAttribute("aria-checked", "true");
|
|
});
|
|
|
|
it("renders the canonical model label instead of the provider-native value", () => {
|
|
render(
|
|
<TuttiModePlanPanel
|
|
assignmentCatalog={assignmentCatalog()}
|
|
assignmentDrafts={{}}
|
|
labels={labels}
|
|
panel={{
|
|
...panel,
|
|
tasks: [
|
|
{
|
|
...panel.tasks[0]!,
|
|
model: "composer-2.5[fast=true]"
|
|
}
|
|
]
|
|
}}
|
|
submitting={false}
|
|
onAssignmentDraftChange={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByLabelText("Model")).toHaveTextContent("composer-2.5");
|
|
expect(screen.getByLabelText("Model")).not.toHaveTextContent("[fast=true]");
|
|
});
|
|
|
|
it("stays read-only without a host-owned draft store", () => {
|
|
render(
|
|
<TuttiModePlanPanel
|
|
assignmentCatalog={assignmentCatalog()}
|
|
labels={labels}
|
|
panel={panel}
|
|
submitting={false}
|
|
/>
|
|
);
|
|
|
|
expect(
|
|
screen.queryByTestId("tutti-plan-task-assignment-implement")
|
|
).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("stays read-only for non-actionable panels even with a catalog", () => {
|
|
render(
|
|
<TuttiModePlanPanel
|
|
assignmentCatalog={assignmentCatalog()}
|
|
assignmentDrafts={{}}
|
|
labels={labels}
|
|
panel={{ ...panel, state: "accepted", actionable: false }}
|
|
submitting={false}
|
|
onAssignmentDraftChange={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
expect(
|
|
screen.queryByTestId("tutti-plan-task-assignment-implement")
|
|
).not.toBeInTheDocument();
|
|
expect(screen.getByTitle("Model")).toHaveTextContent("GPT-5.6 Sol");
|
|
expect(screen.getByTitle("Permission mode")).toHaveTextContent(
|
|
"Accept edits"
|
|
);
|
|
expect(screen.getByTitle("Reasoning effort")).toHaveTextContent("High");
|
|
});
|
|
});
|