feat(FN-2382): finalize presets-first new-agent tab flow
- Split New Agent step-0 into Presets and Custom tabs with presets-first default behavior - Refine tab layout and styling in dashboard styles to address review feedback and improve responsive presentation - Expand NewAgentDialog and AgentsView tests to cover tab switching flows and related route behavior - Update agent documentation to reflect the revised new-agent dialog flow
This commit is contained in:
@@ -35,8 +35,11 @@ interface RuntimeConfig {
|
||||
maxTurns: number;
|
||||
}
|
||||
|
||||
type StepZeroTab = "presets" | "custom";
|
||||
|
||||
export function NewAgentDialog({ isOpen, onClose, onCreated, projectId }: NewAgentDialogProps) {
|
||||
const [step, setStep] = useState(0);
|
||||
const [stepZeroTab, setStepZeroTab] = useState<StepZeroTab>("presets");
|
||||
const [name, setName] = useState("");
|
||||
const [title, setTitle] = useState("");
|
||||
const [icon, setIcon] = useState("");
|
||||
@@ -181,6 +184,7 @@ export function NewAgentDialog({ isOpen, onClose, onCreated, projectId }: NewAge
|
||||
|
||||
const handleClose = () => {
|
||||
setStep(0);
|
||||
setStepZeroTab("presets");
|
||||
setName("");
|
||||
setTitle("");
|
||||
setIcon("");
|
||||
@@ -265,161 +269,208 @@ export function NewAgentDialog({ isOpen, onClose, onCreated, projectId }: NewAge
|
||||
<div className="agent-dialog-body">
|
||||
{step === 0 && (
|
||||
<div>
|
||||
{/* Quick Start Presets */}
|
||||
<div className="agent-presets">
|
||||
<div className="agent-presets-header">
|
||||
Choose a preset or fill in details manually
|
||||
</div>
|
||||
<div className="agent-presets-grid">
|
||||
{AGENT_PRESETS.map(preset => (
|
||||
<button
|
||||
key={preset.id}
|
||||
type="button"
|
||||
className={`agent-preset-card${selectedPresetId === preset.id ? " selected" : ""}`}
|
||||
data-testid={`preset-${preset.id}`}
|
||||
onClick={() => handlePresetSelect(preset)}
|
||||
title={preset.title}
|
||||
>
|
||||
<span className="agent-preset-icon">{preset.icon}</span>
|
||||
<span className="agent-preset-name">{preset.name}</span>
|
||||
<span className="agent-preset-role">{preset.role}</span>
|
||||
{preset.description && (
|
||||
<span className="agent-preset-description">{preset.description}</span>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="agent-dialog-tabs" role="tablist" aria-label="Agent setup mode">
|
||||
<button
|
||||
id="agent-dialog-tab-presets"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="agent-dialog-panel-presets"
|
||||
aria-selected={stepZeroTab === "presets"}
|
||||
tabIndex={stepZeroTab === "presets" ? 0 : -1}
|
||||
className={`agent-dialog-tab${stepZeroTab === "presets" ? " active" : ""}`}
|
||||
onClick={() => setStepZeroTab("presets")}
|
||||
data-testid="agent-dialog-tab-presets"
|
||||
>
|
||||
Preset personas
|
||||
</button>
|
||||
<button
|
||||
id="agent-dialog-tab-custom"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="agent-dialog-panel-custom"
|
||||
aria-selected={stepZeroTab === "custom"}
|
||||
tabIndex={stepZeroTab === "custom" ? 0 : -1}
|
||||
className={`agent-dialog-tab${stepZeroTab === "custom" ? " active" : ""}`}
|
||||
onClick={() => setStepZeroTab("custom")}
|
||||
data-testid="agent-dialog-tab-custom"
|
||||
>
|
||||
Custom agent
|
||||
</button>
|
||||
</div>
|
||||
<div className="agent-dialog-section">
|
||||
<div className="agent-dialog-section-header">Identity</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-name">Name {!selectedPresetId && <span className="agent-dialog-required">*</span>}</label>
|
||||
<input
|
||||
id="agent-name"
|
||||
type="text"
|
||||
className="input"
|
||||
placeholder="e.g. Frontend Reviewer"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-title">Title <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<input
|
||||
id="agent-title"
|
||||
type="text"
|
||||
className="input"
|
||||
placeholder="e.g. Senior Code Reviewer"
|
||||
value={title}
|
||||
onChange={e => setTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-icon">Icon <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<input
|
||||
id="agent-icon"
|
||||
type="text"
|
||||
className="input"
|
||||
placeholder="e.g. 🤖"
|
||||
value={icon}
|
||||
onChange={e => setIcon(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label>Role</label>
|
||||
<div className="agent-role-grid">
|
||||
{AGENT_ROLES.map(r => (
|
||||
<button
|
||||
key={r.value}
|
||||
type="button"
|
||||
className={`agent-role-option${role === r.value ? " selected" : ""}`}
|
||||
onClick={() => setRole(r.value)}
|
||||
>
|
||||
<span className="agent-role-option-icon">{r.icon}</span>
|
||||
<span className="agent-role-option-label">{r.label}</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
{stepZeroTab === "presets" && (
|
||||
<div
|
||||
id="agent-dialog-panel-presets"
|
||||
className="agent-dialog-tab-panel"
|
||||
role="tabpanel"
|
||||
aria-labelledby="agent-dialog-tab-presets"
|
||||
>
|
||||
<div className="agent-presets">
|
||||
<div className="agent-presets-header">
|
||||
Choose a preset persona to prefill role, identity, soul, and instructions
|
||||
</div>
|
||||
<div className="agent-presets-grid">
|
||||
{AGENT_PRESETS.map(preset => (
|
||||
<button
|
||||
key={preset.id}
|
||||
type="button"
|
||||
className={`agent-preset-card${selectedPresetId === preset.id ? " selected" : ""}`}
|
||||
data-testid={`preset-${preset.id}`}
|
||||
onClick={() => handlePresetSelect(preset)}
|
||||
title={preset.title}
|
||||
>
|
||||
<span className="agent-preset-icon">{preset.icon}</span>
|
||||
<span className="agent-preset-name">{preset.name}</span>
|
||||
<span className="agent-preset-role">{preset.role}</span>
|
||||
{preset.description && (
|
||||
<span className="agent-preset-description">{preset.description}</span>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="agent-dialog-section">
|
||||
<div className="agent-dialog-section-header">Configuration</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-reports-to">Reports To <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<select
|
||||
id="agent-reports-to"
|
||||
className="select"
|
||||
value={reportsTo}
|
||||
onChange={e => setReportsTo(e.target.value)}
|
||||
disabled={managersLoading}
|
||||
>
|
||||
<option value="">No manager</option>
|
||||
{availableManagers.map((manager) => (
|
||||
<option key={manager.id} value={manager.id}>
|
||||
{manager.name} ({manager.id})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-soul">Soul <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<textarea
|
||||
id="agent-soul"
|
||||
className="input"
|
||||
rows={2}
|
||||
placeholder="Describe the agent's personality and communication style..."
|
||||
value={soul}
|
||||
onChange={e => setSoul(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-memory">Agent Memory <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<textarea
|
||||
id="agent-memory"
|
||||
className="input"
|
||||
rows={2}
|
||||
placeholder="Private to this agent — durable preferences, operating habits, and context it should carry across tasks..."
|
||||
value={memory}
|
||||
onChange={e => setMemory(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-instructions-path">Instructions Path <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<input
|
||||
id="agent-instructions-path"
|
||||
type="text"
|
||||
className="input"
|
||||
placeholder="e.g. .fusion/agents/reviewer.md"
|
||||
value={instructionsPath}
|
||||
onChange={e => setInstructionsPath(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-instructions-text">Inline Instructions <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<textarea
|
||||
id="agent-instructions-text"
|
||||
className="input"
|
||||
rows={4}
|
||||
placeholder="Add custom behavior instructions..."
|
||||
value={instructionsText}
|
||||
onChange={e => setInstructionsText(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* AI-assisted generation */}
|
||||
<div className="agent-dialog-ai-generate">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn--ai-generate"
|
||||
onClick={() => setIsGenerationModalOpen(true)}
|
||||
)}
|
||||
|
||||
{stepZeroTab === "custom" && (
|
||||
<div
|
||||
id="agent-dialog-panel-custom"
|
||||
className="agent-dialog-tab-panel"
|
||||
role="tabpanel"
|
||||
aria-labelledby="agent-dialog-tab-custom"
|
||||
>
|
||||
<span>✨</span>
|
||||
Generate with AI
|
||||
</button>
|
||||
<p className="agent-dialog-ai-hint">
|
||||
Describe your agent's role and let AI generate a specification
|
||||
</p>
|
||||
</div>
|
||||
<div className="agent-dialog-section">
|
||||
<div className="agent-dialog-section-header">Identity</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-name">Name {!selectedPresetId && <span className="agent-dialog-required">*</span>}</label>
|
||||
<input
|
||||
id="agent-name"
|
||||
type="text"
|
||||
className="input"
|
||||
placeholder="e.g. Frontend Reviewer"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-title">Title <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<input
|
||||
id="agent-title"
|
||||
type="text"
|
||||
className="input"
|
||||
placeholder="e.g. Senior Code Reviewer"
|
||||
value={title}
|
||||
onChange={e => setTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-icon">Icon <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<input
|
||||
id="agent-icon"
|
||||
type="text"
|
||||
className="input"
|
||||
placeholder="e.g. 🤖"
|
||||
value={icon}
|
||||
onChange={e => setIcon(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label>Role</label>
|
||||
<div className="agent-role-grid">
|
||||
{AGENT_ROLES.map(r => (
|
||||
<button
|
||||
key={r.value}
|
||||
type="button"
|
||||
className={`agent-role-option${role === r.value ? " selected" : ""}`}
|
||||
onClick={() => setRole(r.value)}
|
||||
>
|
||||
<span className="agent-role-option-icon">{r.icon}</span>
|
||||
<span className="agent-role-option-label">{r.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="agent-dialog-section">
|
||||
<div className="agent-dialog-section-header">Configuration</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-reports-to">Reports To <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<select
|
||||
id="agent-reports-to"
|
||||
className="select"
|
||||
value={reportsTo}
|
||||
onChange={e => setReportsTo(e.target.value)}
|
||||
disabled={managersLoading}
|
||||
>
|
||||
<option value="">No manager</option>
|
||||
{availableManagers.map((manager) => (
|
||||
<option key={manager.id} value={manager.id}>
|
||||
{manager.name} ({manager.id})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-soul">Soul <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<textarea
|
||||
id="agent-soul"
|
||||
className="input"
|
||||
rows={2}
|
||||
placeholder="Describe the agent's personality and communication style..."
|
||||
value={soul}
|
||||
onChange={e => setSoul(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-memory">Agent Memory <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<textarea
|
||||
id="agent-memory"
|
||||
className="input"
|
||||
rows={2}
|
||||
placeholder="Private to this agent — durable preferences, operating habits, and context it should carry across tasks..."
|
||||
value={memory}
|
||||
onChange={e => setMemory(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-instructions-path">Instructions Path <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<input
|
||||
id="agent-instructions-path"
|
||||
type="text"
|
||||
className="input"
|
||||
placeholder="e.g. .fusion/agents/reviewer.md"
|
||||
value={instructionsPath}
|
||||
onChange={e => setInstructionsPath(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="agent-dialog-field">
|
||||
<label htmlFor="agent-instructions-text">Inline Instructions <span className="agent-dialog-optional">(optional)</span></label>
|
||||
<textarea
|
||||
id="agent-instructions-text"
|
||||
className="input"
|
||||
rows={4}
|
||||
placeholder="Add custom behavior instructions..."
|
||||
value={instructionsText}
|
||||
onChange={e => setInstructionsText(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* AI-assisted generation */}
|
||||
<div className="agent-dialog-ai-generate">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn--ai-generate"
|
||||
onClick={() => setIsGenerationModalOpen(true)}
|
||||
>
|
||||
<span>✨</span>
|
||||
Generate with AI
|
||||
</button>
|
||||
<p className="agent-dialog-ai-hint">
|
||||
Describe your agent's role and let AI generate a specification
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -939,7 +939,8 @@ describe("AgentsView", () => {
|
||||
// Open create dialog
|
||||
fireEvent.click(screen.getByText("New Agent"));
|
||||
|
||||
// Step 0: Fill in agent name
|
||||
// Step 0: switch to Custom tab and fill in agent name
|
||||
fireEvent.click(screen.getByRole("tab", { name: "Custom agent" }));
|
||||
const nameInput = screen.getByPlaceholderText("e.g. Frontend Reviewer");
|
||||
fireEvent.change(nameInput, { target: { value: "My Agent" } });
|
||||
|
||||
@@ -972,10 +973,12 @@ describe("AgentsView", () => {
|
||||
|
||||
fireEvent.click(screen.getByText("New Agent"));
|
||||
|
||||
// Wait for the dialog to settle after the model fetch completes
|
||||
// Presets tab is default and custom fields appear after switching tabs
|
||||
await waitFor(() => {
|
||||
expect(screen.getByPlaceholderText("e.g. Frontend Reviewer")).toBeTruthy();
|
||||
expect(screen.getByRole("tab", { name: "Preset personas", selected: true })).toBeTruthy();
|
||||
});
|
||||
fireEvent.click(screen.getByRole("tab", { name: "Custom agent" }));
|
||||
expect(screen.getByPlaceholderText("e.g. Frontend Reviewer")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("does not allow proceeding with empty name", async () => {
|
||||
@@ -1009,6 +1012,7 @@ describe("AgentsView", () => {
|
||||
|
||||
fireEvent.click(screen.getByText("New Agent"));
|
||||
|
||||
fireEvent.click(screen.getByRole("tab", { name: "Custom agent" }));
|
||||
const nameInput = screen.getByPlaceholderText("e.g. Frontend Reviewer");
|
||||
fireEvent.change(nameInput, { target: { value: "Fail Agent" } });
|
||||
|
||||
|
||||
@@ -134,6 +134,23 @@ function clickModelOption(portal: HTMLElement, optionText: RegExp) {
|
||||
fireEvent.click(option!);
|
||||
}
|
||||
|
||||
async function openPresetTab(user: ReturnType<typeof userEvent.setup>) {
|
||||
await user.click(screen.getByRole("tab", { name: "Preset personas" }));
|
||||
}
|
||||
|
||||
async function openCustomTab(user: ReturnType<typeof userEvent.setup>) {
|
||||
await user.click(screen.getByRole("tab", { name: "Custom agent" }));
|
||||
}
|
||||
|
||||
function openCustomTabSync() {
|
||||
fireEvent.click(screen.getByRole("tab", { name: "Custom agent" }));
|
||||
}
|
||||
|
||||
function getStepZeroField(label: string | RegExp) {
|
||||
openCustomTabSync();
|
||||
return screen.getByLabelText(label);
|
||||
}
|
||||
|
||||
describe("NewAgentDialog", () => {
|
||||
const mockOnClose = vi.fn();
|
||||
const mockOnCreated = vi.fn();
|
||||
@@ -164,17 +181,50 @@ describe("NewAgentDialog", () => {
|
||||
expect(screen.getByRole("dialog", { name: "Create new agent" })).toBeTruthy();
|
||||
});
|
||||
|
||||
it("shows Identity and Configuration section headers on step 0", async () => {
|
||||
it("renders tabbed step-0 UI with preset tab active by default", async () => {
|
||||
await act(async () => {
|
||||
render(
|
||||
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
|
||||
);
|
||||
});
|
||||
|
||||
expect(screen.getByText("Identity")).toBeInTheDocument();
|
||||
expect(screen.getByText("Configuration")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/Name/)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/Reports To/)).toBeInTheDocument();
|
||||
const tabList = screen.getByRole("tablist", { name: "Agent setup mode" });
|
||||
expect(tabList).toBeInTheDocument();
|
||||
|
||||
const customTab = screen.getByRole("tab", { name: "Custom agent" });
|
||||
const presetsTab = screen.getByRole("tab", { name: "Preset personas" });
|
||||
|
||||
expect(presetsTab).toHaveAttribute("aria-selected", "true");
|
||||
expect(presetsTab).toHaveAttribute("aria-controls", "agent-dialog-panel-presets");
|
||||
expect(customTab).toHaveAttribute("aria-selected", "false");
|
||||
expect(customTab).toHaveAttribute("aria-controls", "agent-dialog-panel-custom");
|
||||
|
||||
expect(screen.getByRole("tabpanel", { name: "Preset personas" })).toBeInTheDocument();
|
||||
expect(screen.getByTestId("preset-ceo")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Identity")).toBeNull();
|
||||
expect(screen.queryByLabelText(/Name/)).toBeNull();
|
||||
});
|
||||
|
||||
it("switches between tabs and preserves manual values", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
await openCustomTab(user);
|
||||
await user.type(screen.getByLabelText(/Name/), "Custom Value");
|
||||
await openPresetTab(user);
|
||||
|
||||
expect(screen.getByRole("tabpanel", { name: "Preset personas" })).toBeInTheDocument();
|
||||
expect(screen.getByTestId("preset-ceo")).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText(/Name/)).toBeNull();
|
||||
|
||||
await openCustomTab(user);
|
||||
|
||||
const nameInput = getStepZeroField(/Name/) as HTMLInputElement;
|
||||
expect(nameInput.value).toBe("Custom Value");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -198,7 +248,7 @@ describe("NewAgentDialog", () => {
|
||||
expect(mockFetchAgents).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
const reportsToSelect = screen.getByLabelText(/Reports To/) as HTMLSelectElement;
|
||||
const reportsToSelect = getStepZeroField(/Reports To/) as HTMLSelectElement;
|
||||
expect(reportsToSelect.tagName).toBe("SELECT");
|
||||
expect(within(reportsToSelect).getByRole("option", { name: "No manager" })).toBeTruthy();
|
||||
expect(within(reportsToSelect).getByRole("option", { name: "Manager One (agent-manager-1)" })).toBeTruthy();
|
||||
@@ -213,10 +263,10 @@ describe("NewAgentDialog", () => {
|
||||
|
||||
await waitFor(() => expect(mockFetchAgents).toHaveBeenCalledOnce());
|
||||
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Agent With Manager");
|
||||
|
||||
const reportsToSelect = screen.getByLabelText(/Reports To/);
|
||||
const reportsToSelect = getStepZeroField(/Reports To/);
|
||||
await user.selectOptions(reportsToSelect, "agent-manager-1");
|
||||
|
||||
await user.click(screen.getByText("Next"));
|
||||
@@ -245,7 +295,7 @@ describe("NewAgentDialog", () => {
|
||||
|
||||
await waitFor(() => expect(mockFetchAgents).toHaveBeenCalledOnce());
|
||||
|
||||
await user.type(screen.getByLabelText(/Name/), "Agent Without Manager");
|
||||
await user.type(getStepZeroField(/Name/), "Agent Without Manager");
|
||||
await user.click(screen.getByText("Next"));
|
||||
await user.click(screen.getByText("Next"));
|
||||
await user.click(screen.getByText("Create"));
|
||||
@@ -269,11 +319,11 @@ describe("NewAgentDialog", () => {
|
||||
expect(mockFetchAgents).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
const reportsToSelect = screen.getByLabelText(/Reports To/) as HTMLSelectElement;
|
||||
const reportsToSelect = getStepZeroField(/Reports To/) as HTMLSelectElement;
|
||||
expect(within(reportsToSelect).getByRole("option", { name: "No manager" })).toBeTruthy();
|
||||
expect(reportsToSelect.options).toHaveLength(1);
|
||||
|
||||
await user.type(screen.getByLabelText(/Name/), "Agent Works Without Managers");
|
||||
await user.type(getStepZeroField(/Name/), "Agent Works Without Managers");
|
||||
await user.click(screen.getByText("Next"));
|
||||
await user.click(screen.getByText("Next"));
|
||||
await user.click(screen.getByText("Create"));
|
||||
@@ -306,7 +356,7 @@ describe("NewAgentDialog", () => {
|
||||
);
|
||||
|
||||
// Navigate to step 1 (model config step) by filling name and clicking Next
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await fireEvent.change(nameInput, { target: { value: "Test Agent" } });
|
||||
await fireEvent.click(screen.getByText("Next"));
|
||||
|
||||
@@ -332,7 +382,7 @@ describe("NewAgentDialog", () => {
|
||||
});
|
||||
|
||||
// Navigate to step 1
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
await user.click(screen.getByText("Next"));
|
||||
|
||||
@@ -353,7 +403,7 @@ describe("NewAgentDialog", () => {
|
||||
});
|
||||
|
||||
// Navigate to step 1
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
await user.click(screen.getByText("Next"));
|
||||
|
||||
@@ -372,7 +422,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Navigate to step 1
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
await user.click(screen.getByText("Next"));
|
||||
|
||||
@@ -398,7 +448,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Navigate to step 2
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
await user.click(screen.getByText("Next"));
|
||||
await user.click(screen.getByText("Next"));
|
||||
@@ -418,7 +468,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Navigate to step 1
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
await user.click(screen.getByText("Next"));
|
||||
|
||||
@@ -445,7 +495,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Step 0: Fill name
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
|
||||
// Step 1: Navigate and select model
|
||||
@@ -477,7 +527,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Step 0: Fill name
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
|
||||
// Step 1: Leave model as default
|
||||
@@ -506,7 +556,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Step 0: Fill name
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Thinking Agent");
|
||||
|
||||
// Step 1: Select model and thinking level
|
||||
@@ -545,7 +595,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Navigate to step 1 — should still show the dropdown (empty models)
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
await user.click(screen.getByText("Next"));
|
||||
|
||||
@@ -564,7 +614,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Fill in name
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Agent Name");
|
||||
|
||||
// Navigate to step 1 and select model
|
||||
@@ -588,7 +638,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledTimes(2));
|
||||
|
||||
// Name should be empty
|
||||
const newNameInput = screen.getByLabelText(/Name/) as HTMLInputElement;
|
||||
const newNameInput = getStepZeroField(/Name/) as HTMLInputElement;
|
||||
expect(newNameInput.value).toBe("");
|
||||
});
|
||||
});
|
||||
@@ -600,6 +650,7 @@ describe("NewAgentDialog", () => {
|
||||
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
|
||||
);
|
||||
});
|
||||
openCustomTabSync();
|
||||
expect(screen.getByText("Generate with AI")).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -615,6 +666,7 @@ describe("NewAgentDialog", () => {
|
||||
expect(screen.queryByTestId("agent-generation-modal")).toBeNull();
|
||||
|
||||
// Click the Generate with AI button
|
||||
openCustomTabSync();
|
||||
await user.click(screen.getByText("Generate with AI"));
|
||||
|
||||
// Generation modal should now be open
|
||||
@@ -630,6 +682,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Open generation modal and apply spec
|
||||
openCustomTabSync();
|
||||
await user.click(screen.getByText("Generate with AI"));
|
||||
await user.click(screen.getByTestId("generation-modal-apply"));
|
||||
|
||||
@@ -656,6 +709,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Open generation modal and apply spec with role "reviewer"
|
||||
openCustomTabSync();
|
||||
await user.click(screen.getByText("Generate with AI"));
|
||||
await user.click(screen.getByTestId("generation-modal-apply"));
|
||||
|
||||
@@ -676,6 +730,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Open generation modal and apply spec with unknown role "security-auditor"
|
||||
openCustomTabSync();
|
||||
await user.click(screen.getByText("Generate with AI"));
|
||||
await user.click(screen.getByTestId("generation-modal-apply-custom-role"));
|
||||
|
||||
@@ -696,6 +751,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Open generation modal and apply spec
|
||||
openCustomTabSync();
|
||||
await user.click(screen.getByText("Generate with AI"));
|
||||
await user.click(screen.getByTestId("generation-modal-apply"));
|
||||
|
||||
@@ -714,10 +770,11 @@ describe("NewAgentDialog", () => {
|
||||
);
|
||||
|
||||
// Fill in a name first
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Manual Name");
|
||||
|
||||
// Open generation modal
|
||||
openCustomTabSync();
|
||||
await user.click(screen.getByText("Generate with AI"));
|
||||
expect(screen.getByTestId("agent-generation-modal")).toBeTruthy();
|
||||
|
||||
@@ -725,7 +782,7 @@ describe("NewAgentDialog", () => {
|
||||
await user.click(screen.getByTestId("generation-modal-close"));
|
||||
|
||||
// Should still be on step 0 with original name
|
||||
const nameAfter = screen.getByLabelText(/Name/) as HTMLInputElement;
|
||||
const nameAfter = getStepZeroField(/Name/) as HTMLInputElement;
|
||||
expect(nameAfter.value).toBe("Manual Name");
|
||||
expect(screen.queryByTestId("agent-generation-modal")).toBeNull();
|
||||
});
|
||||
@@ -739,6 +796,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Open generation modal and apply spec
|
||||
openCustomTabSync();
|
||||
await user.click(screen.getByText("Generate with AI"));
|
||||
await user.click(screen.getByTestId("generation-modal-apply"));
|
||||
|
||||
@@ -763,23 +821,31 @@ describe("NewAgentDialog", () => {
|
||||
});
|
||||
|
||||
describe("preset selection", () => {
|
||||
it("renders all 20 preset cards in step 0", async () => {
|
||||
it("renders all 20 preset cards in the preset tab", async () => {
|
||||
const user = userEvent.setup();
|
||||
await act(async () => {
|
||||
render(
|
||||
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
|
||||
);
|
||||
});
|
||||
|
||||
await openPresetTab(user);
|
||||
|
||||
const presetCards = screen.getAllByTestId(/^preset-/);
|
||||
expect(presetCards).toHaveLength(20);
|
||||
});
|
||||
|
||||
it("shows the quick start header text", async () => {
|
||||
it("shows the preset tab header text", async () => {
|
||||
const user = userEvent.setup();
|
||||
await act(async () => {
|
||||
render(
|
||||
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
|
||||
);
|
||||
});
|
||||
expect(screen.getByText("Choose a preset or fill in details manually")).toBeTruthy();
|
||||
|
||||
await openPresetTab(user);
|
||||
|
||||
expect(screen.getByText("Choose a preset persona to prefill role, identity, soul, and instructions")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("clicking a preset populates name, title, icon, and role", async () => {
|
||||
@@ -791,16 +857,18 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Click the "Engineer" preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-engineer"));
|
||||
|
||||
// Should advance to step 1 (model config), go back to verify fields
|
||||
await user.click(screen.getByText("Back"));
|
||||
await openCustomTab(user);
|
||||
|
||||
// Verify form fields were populated
|
||||
const nameInput = screen.getByLabelText(/Name/) as HTMLInputElement;
|
||||
const nameInput = getStepZeroField(/Name/) as HTMLInputElement;
|
||||
expect(nameInput.value).toBe("Engineer");
|
||||
|
||||
const titleInput = screen.getByLabelText(/Title/) as HTMLInputElement;
|
||||
const titleInput = getStepZeroField(/Title/) as HTMLInputElement;
|
||||
// Title should be set to the preset's description (not the professional title)
|
||||
expect(titleInput.value).toBe("Implements features, fixes bugs, and writes well-tested code across the full application stack.");
|
||||
|
||||
@@ -819,6 +887,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Click the "CTO" preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-cto"));
|
||||
|
||||
// Should be on step 1 — model dropdown visible
|
||||
@@ -834,6 +903,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Click the "CEO" preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-ceo"));
|
||||
|
||||
// Go back to step 0 to verify visual feedback
|
||||
@@ -852,10 +922,12 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Click CEO preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-ceo"));
|
||||
await user.click(screen.getByText("Back"));
|
||||
|
||||
// Click CTO preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-cto"));
|
||||
await user.click(screen.getByText("Back"));
|
||||
|
||||
@@ -864,7 +936,8 @@ describe("NewAgentDialog", () => {
|
||||
expect(screen.getByTestId("preset-ceo").classList.contains("selected")).toBe(false);
|
||||
|
||||
// Name should be updated
|
||||
const nameInput = screen.getByLabelText(/Name/) as HTMLInputElement;
|
||||
await openCustomTab(user);
|
||||
const nameInput = getStepZeroField(/Name/) as HTMLInputElement;
|
||||
expect(nameInput.value).toBe("CTO");
|
||||
});
|
||||
|
||||
@@ -877,11 +950,13 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Select a preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-engineer"));
|
||||
await user.click(screen.getByText("Back"));
|
||||
await openCustomTab(user);
|
||||
|
||||
// Override the name manually
|
||||
const nameInput = screen.getByLabelText(/Name/) as HTMLInputElement;
|
||||
const nameInput = getStepZeroField(/Name/) as HTMLInputElement;
|
||||
await user.clear(nameInput);
|
||||
await user.type(nameInput, "My Custom Engineer");
|
||||
|
||||
@@ -897,6 +972,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Select a preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-ceo"));
|
||||
|
||||
// Close the dialog
|
||||
@@ -914,10 +990,11 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledTimes(2));
|
||||
|
||||
// Name should be empty (no preset selected)
|
||||
const nameInput = screen.getByLabelText(/Name/) as HTMLInputElement;
|
||||
const nameInput = getStepZeroField(/Name/) as HTMLInputElement;
|
||||
expect(nameInput.value).toBe("");
|
||||
|
||||
// No preset cards should be selected
|
||||
await openPresetTab(user);
|
||||
const selectedCards = document.querySelectorAll(".agent-preset-card.selected");
|
||||
expect(selectedCards).toHaveLength(0);
|
||||
});
|
||||
@@ -931,6 +1008,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Click the "Reviewer" preset (advances to step 1)
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-reviewer"));
|
||||
|
||||
// Step 1: navigate to summary
|
||||
@@ -964,12 +1042,15 @@ describe("NewAgentDialog", () => {
|
||||
});
|
||||
|
||||
it("preset card titles show the professional title", async () => {
|
||||
const user = userEvent.setup();
|
||||
await act(async () => {
|
||||
render(
|
||||
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
|
||||
);
|
||||
});
|
||||
|
||||
await openPresetTab(user);
|
||||
|
||||
const ceoCard = screen.getByTestId("preset-ceo");
|
||||
expect(ceoCard.getAttribute("title")).toBe("Chief Executive Officer");
|
||||
|
||||
@@ -978,12 +1059,15 @@ describe("NewAgentDialog", () => {
|
||||
});
|
||||
|
||||
it("renders descriptions in all 20 preset cards", async () => {
|
||||
const user = userEvent.setup();
|
||||
await act(async () => {
|
||||
render(
|
||||
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
|
||||
);
|
||||
});
|
||||
|
||||
await openPresetTab(user);
|
||||
|
||||
// Every preset should have a description element rendered
|
||||
const descriptionElements = screen.getAllByText(/.\./, {
|
||||
selector: ".agent-preset-description",
|
||||
@@ -998,12 +1082,15 @@ describe("NewAgentDialog", () => {
|
||||
|
||||
it("all presets have non-empty description strings", async () => {
|
||||
// Import the array directly by checking the rendered cards
|
||||
const user = userEvent.setup();
|
||||
await act(async () => {
|
||||
render(
|
||||
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
|
||||
);
|
||||
});
|
||||
|
||||
await openPresetTab(user);
|
||||
|
||||
const presetIds = [
|
||||
"ceo", "cto", "cmo", "cfo", "engineer", "backend-engineer",
|
||||
"frontend-engineer", "fullstack-engineer", "qa-engineer",
|
||||
@@ -1029,12 +1116,14 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Click the CEO preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-ceo"));
|
||||
|
||||
// Go back to verify the title field
|
||||
await user.click(screen.getByText("Back"));
|
||||
await openCustomTab(user);
|
||||
|
||||
const titleInput = screen.getByLabelText(/Title/) as HTMLInputElement;
|
||||
const titleInput = getStepZeroField(/Title/) as HTMLInputElement;
|
||||
expect(titleInput.value).toBe("Oversees project strategy, sets priorities, and coordinates between departments to ensure alignment with business goals.");
|
||||
});
|
||||
|
||||
@@ -1046,6 +1135,7 @@ describe("NewAgentDialog", () => {
|
||||
});
|
||||
|
||||
// On initial render (step 0, no preset), the * required indicator should be visible
|
||||
openCustomTabSync();
|
||||
const nameLabel = screen.getByText("Name", { selector: "label" });
|
||||
const requiredSpan = nameLabel.querySelector(".agent-dialog-required");
|
||||
expect(requiredSpan).toBeTruthy();
|
||||
@@ -1061,10 +1151,12 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Select a preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-engineer"));
|
||||
|
||||
// Preset advances to step 1, go back to step 0
|
||||
await user.click(screen.getByText("Back"));
|
||||
await openCustomTab(user);
|
||||
|
||||
// The * required indicator should NOT be visible when a preset is selected
|
||||
const nameLabel = screen.getByText("Name", { selector: "label" });
|
||||
@@ -1081,13 +1173,15 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Select a preset (this fills the name and advances to step 1)
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-ceo"));
|
||||
|
||||
// Go back to step 0
|
||||
await user.click(screen.getByText("Back"));
|
||||
await openCustomTab(user);
|
||||
|
||||
// Clear the name field
|
||||
const nameInput = screen.getByLabelText(/Name/) as HTMLInputElement;
|
||||
const nameInput = getStepZeroField(/Name/) as HTMLInputElement;
|
||||
await user.clear(nameInput);
|
||||
expect(nameInput.value).toBe("");
|
||||
|
||||
@@ -1115,6 +1209,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Click the QA Engineer preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-qa-engineer"));
|
||||
|
||||
// Navigate to summary and create
|
||||
@@ -1143,13 +1238,15 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Select a preset (advances to step 1)
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-engineer"));
|
||||
|
||||
// Go back to step 0 to override instructions
|
||||
await user.click(screen.getByText("Back"));
|
||||
await openCustomTab(user);
|
||||
|
||||
// Override the instructionsText manually
|
||||
const instructionsTextarea = screen.getByLabelText(/Inline Instructions/) as HTMLTextAreaElement;
|
||||
const instructionsTextarea = getStepZeroField(/Inline Instructions/) as HTMLTextAreaElement;
|
||||
await user.clear(instructionsTextarea);
|
||||
await user.type(instructionsTextarea, "My custom instructions");
|
||||
|
||||
@@ -1178,17 +1275,19 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Click the CTO preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-cto"));
|
||||
|
||||
// Go back to step 0 to verify fields
|
||||
await user.click(screen.getByText("Back"));
|
||||
await openCustomTab(user);
|
||||
|
||||
// Verify soul was populated
|
||||
const soulTextarea = screen.getByLabelText(/Soul/) as HTMLTextAreaElement;
|
||||
const soulTextarea = getStepZeroField(/Soul/) as HTMLTextAreaElement;
|
||||
expect(soulTextarea.value).toContain("pragmatic technologist");
|
||||
|
||||
// Verify instructionsText was populated
|
||||
const instructionsTextarea = screen.getByLabelText(/Inline Instructions/) as HTMLTextAreaElement;
|
||||
const instructionsTextarea = getStepZeroField(/Inline Instructions/) as HTMLTextAreaElement;
|
||||
expect(instructionsTextarea.value).toContain("Evaluate technology choices");
|
||||
});
|
||||
|
||||
@@ -1201,23 +1300,27 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Select CEO preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-ceo"));
|
||||
await user.click(screen.getByText("Back"));
|
||||
|
||||
// Verify CEO soul is set
|
||||
let soulTextarea = screen.getByLabelText(/Soul/) as HTMLTextAreaElement;
|
||||
await openCustomTab(user);
|
||||
let soulTextarea = getStepZeroField(/Soul/) as HTMLTextAreaElement;
|
||||
expect(soulTextarea.value).toContain("strategic leader");
|
||||
|
||||
// Select a different preset (DevOps Engineer)
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-devops-engineer"));
|
||||
await user.click(screen.getByText("Back"));
|
||||
await openCustomTab(user);
|
||||
|
||||
// Verify soul updated to DevOps
|
||||
soulTextarea = screen.getByLabelText(/Soul/) as HTMLTextAreaElement;
|
||||
soulTextarea = getStepZeroField(/Soul/) as HTMLTextAreaElement;
|
||||
expect(soulTextarea.value).toContain("infrastructure-minded engineer");
|
||||
|
||||
// Verify instructions updated
|
||||
const instructionsTextarea = screen.getByLabelText(/Inline Instructions/) as HTMLTextAreaElement;
|
||||
const instructionsTextarea = getStepZeroField(/Inline Instructions/) as HTMLTextAreaElement;
|
||||
expect(instructionsTextarea.value).toContain("rollback plan");
|
||||
});
|
||||
|
||||
@@ -1230,6 +1333,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Select a preset
|
||||
await openPresetTab(user);
|
||||
await user.click(screen.getByTestId("preset-cto"));
|
||||
|
||||
// Close the dialog — wait for the state updates to flush before unmounting
|
||||
@@ -1245,10 +1349,10 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledTimes(2));
|
||||
|
||||
// Soul and instructionsText should be empty
|
||||
const soulTextarea = screen.getByLabelText(/Soul/) as HTMLTextAreaElement;
|
||||
const soulTextarea = getStepZeroField(/Soul/) as HTMLTextAreaElement;
|
||||
expect(soulTextarea.value).toBe("");
|
||||
|
||||
const instructionsTextarea = screen.getByLabelText(/Inline Instructions/) as HTMLTextAreaElement;
|
||||
const instructionsTextarea = getStepZeroField(/Inline Instructions/) as HTMLTextAreaElement;
|
||||
expect(instructionsTextarea.value).toBe("");
|
||||
});
|
||||
});
|
||||
@@ -1268,7 +1372,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
const user = userEvent.setup();
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
await user.click(screen.getByText("Next"));
|
||||
|
||||
@@ -1300,7 +1404,7 @@ describe("NewAgentDialog", () => {
|
||||
});
|
||||
|
||||
const user = userEvent.setup();
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
await user.click(screen.getByText("Next"));
|
||||
|
||||
@@ -1327,7 +1431,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Navigate to step 1
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
await user.click(screen.getByText("Next"));
|
||||
|
||||
@@ -1344,7 +1448,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Navigate to step 1 and add a skill
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
await user.click(screen.getByText("Next"));
|
||||
|
||||
@@ -1367,7 +1471,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Navigate to step 1
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
await user.click(screen.getByText("Next"));
|
||||
|
||||
@@ -1396,7 +1500,7 @@ describe("NewAgentDialog", () => {
|
||||
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
|
||||
|
||||
// Navigate through steps without adding skills
|
||||
const nameInput = screen.getByLabelText(/Name/);
|
||||
const nameInput = getStepZeroField(/Name/);
|
||||
await user.type(nameInput, "Test Agent");
|
||||
await user.click(screen.getByText("Next"));
|
||||
await user.click(screen.getByText("Next"));
|
||||
|
||||
Reference in New Issue
Block a user