mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 09:41:04 +01:00
Test agent config environment selector
Cover environment override visibility and copy for Local-only and Local plus runnable non-local environment setups. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
03bbe3e15c
commit
7a74d93579
1 file changed
+249
@@ -0,0 +1,249 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { createRoot, type Root } from "react-dom/client";
|
||||
import { act } from "react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { Agent, Environment } from "@paperclipai/shared";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { AgentConfigForm } from "./AgentConfigForm";
|
||||
|
||||
const mockAgentsApi = vi.hoisted(() => ({
|
||||
adapterModels: vi.fn(),
|
||||
detectModel: vi.fn(),
|
||||
list: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockEnvironmentsApi = vi.hoisted(() => ({
|
||||
list: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockInstanceSettingsApi = vi.hoisted(() => ({
|
||||
get: vi.fn(),
|
||||
getExperimental: vi.fn(),
|
||||
getGeneral: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockSecretsApi = vi.hoisted(() => ({
|
||||
list: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../api/agents", () => ({
|
||||
agentsApi: mockAgentsApi,
|
||||
}));
|
||||
|
||||
vi.mock("../api/environments", () => ({
|
||||
environmentsApi: mockEnvironmentsApi,
|
||||
}));
|
||||
|
||||
vi.mock("../api/instanceSettings", () => ({
|
||||
instanceSettingsApi: mockInstanceSettingsApi,
|
||||
}));
|
||||
|
||||
vi.mock("../api/secrets", () => ({
|
||||
secretsApi: mockSecretsApi,
|
||||
}));
|
||||
|
||||
vi.mock("../context/CompanyContext", () => ({
|
||||
useCompany: () => ({
|
||||
companies: [{ id: "company-1", name: "Paperclip" }],
|
||||
selectedCompanyId: "company-1",
|
||||
selectedCompany: { id: "company-1", name: "Paperclip" },
|
||||
selectionSource: "bootstrap",
|
||||
loading: false,
|
||||
error: null,
|
||||
setSelectedCompanyId: vi.fn(),
|
||||
reloadCompanies: vi.fn(),
|
||||
createCompany: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../adapters", () => ({
|
||||
getUIAdapter: () => ({
|
||||
type: "codex_local",
|
||||
label: "Codex",
|
||||
ConfigFields: () => null,
|
||||
buildAdapterConfig: () => ({}),
|
||||
parseStdoutLine: () => [],
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../adapters/use-adapter-capabilities", () => ({
|
||||
useAdapterCapabilities: () => () => ({
|
||||
supportsInstructionsBundle: true,
|
||||
supportsSkills: true,
|
||||
supportsLocalAgentJwt: true,
|
||||
requiresMaterializedRuntimeSkills: false,
|
||||
supportsModelProfiles: true,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../adapters/use-disabled-adapters", () => ({
|
||||
useDisabledAdaptersSync: () => [],
|
||||
}));
|
||||
|
||||
vi.mock("./MarkdownEditor", () => ({
|
||||
MarkdownEditor: ({
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
}: {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
placeholder?: string;
|
||||
}) => (
|
||||
<textarea
|
||||
aria-label={placeholder ?? "Markdown"}
|
||||
value={value}
|
||||
onChange={(event) => onChange(event.currentTarget.value)}
|
||||
/>
|
||||
),
|
||||
}));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
async function flushReact() {
|
||||
await act(async () => {
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
await Promise.resolve();
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 0));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function makeAgent(overrides: Partial<Agent> = {}): Agent {
|
||||
return {
|
||||
id: "agent-1",
|
||||
companyId: "company-1",
|
||||
name: "Cody",
|
||||
role: "Engineer",
|
||||
title: null,
|
||||
icon: null,
|
||||
status: "idle",
|
||||
reportsTo: null,
|
||||
capabilities: null,
|
||||
adapterType: "codex_local",
|
||||
adapterConfig: {},
|
||||
runtimeConfig: {},
|
||||
defaultEnvironmentId: null,
|
||||
contextMode: "thin",
|
||||
budgetMonthlyCents: 0,
|
||||
spentMonthlyCents: 0,
|
||||
permissions: {},
|
||||
lastHeartbeatAt: null,
|
||||
metadata: null,
|
||||
createdAt: new Date(0),
|
||||
updatedAt: new Date(0),
|
||||
...overrides,
|
||||
} as Agent;
|
||||
}
|
||||
|
||||
function makeEnvironment(overrides: Partial<Environment>): Environment {
|
||||
return {
|
||||
id: "env-1",
|
||||
name: "Local",
|
||||
description: null,
|
||||
driver: "local",
|
||||
status: "active",
|
||||
config: {},
|
||||
envVars: {},
|
||||
metadata: null,
|
||||
createdAt: new Date(0),
|
||||
updatedAt: new Date(0),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
async function renderForm(environments: Environment[]) {
|
||||
mockEnvironmentsApi.list.mockResolvedValue(environments);
|
||||
|
||||
const container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
const root = createRoot(container);
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: { retry: false },
|
||||
mutations: { retry: false },
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
root.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<TooltipProvider>
|
||||
<AgentConfigForm
|
||||
mode="edit"
|
||||
agent={makeAgent()}
|
||||
onSave={vi.fn()}
|
||||
hidePromptTemplate
|
||||
showAdapterTypeField={false}
|
||||
showAdapterTestEnvironmentButton={false}
|
||||
/>
|
||||
</TooltipProvider>
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
});
|
||||
|
||||
await flushReact();
|
||||
return { container, root };
|
||||
}
|
||||
|
||||
describe("AgentConfigForm environment selector", () => {
|
||||
let roots: Root[] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
mockAgentsApi.adapterModels.mockResolvedValue([]);
|
||||
mockAgentsApi.detectModel.mockResolvedValue(null);
|
||||
mockAgentsApi.list.mockResolvedValue([]);
|
||||
mockInstanceSettingsApi.get.mockResolvedValue({ defaultEnvironmentId: null });
|
||||
mockInstanceSettingsApi.getExperimental.mockResolvedValue({ enableEnvironments: true });
|
||||
mockInstanceSettingsApi.getGeneral.mockResolvedValue({ executionMode: "any" });
|
||||
mockSecretsApi.list.mockResolvedValue([]);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
for (const root of roots) {
|
||||
await act(async () => {
|
||||
root.unmount();
|
||||
});
|
||||
}
|
||||
roots = [];
|
||||
document.body.innerHTML = "";
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("hides the environment override when Local is the only configured environment", async () => {
|
||||
const result = await renderForm([
|
||||
makeEnvironment({ id: "local-1", name: "Local", driver: "local" }),
|
||||
]);
|
||||
roots.push(result.root);
|
||||
|
||||
expect(result.container.textContent).not.toContain("Environment override");
|
||||
expect(result.container.querySelector("select")).toBeNull();
|
||||
});
|
||||
|
||||
it("shows concise Environment copy when one runnable non-local environment exists", async () => {
|
||||
const result = await renderForm([
|
||||
makeEnvironment({ id: "local-1", name: "Local", driver: "local" }),
|
||||
makeEnvironment({
|
||||
id: "sandbox-1",
|
||||
name: "E2B",
|
||||
driver: "sandbox",
|
||||
config: { provider: "e2b" },
|
||||
}),
|
||||
]);
|
||||
roots.push(result.root);
|
||||
|
||||
const text = result.container.textContent ?? "";
|
||||
const selector = result.container.querySelector("select");
|
||||
|
||||
expect(text).toContain("Environment");
|
||||
expect(text).toContain("Environment override");
|
||||
expect(selector?.textContent).toContain("Default: Local");
|
||||
expect(selector?.textContent).toContain("E2B · sandbox");
|
||||
expect(text).not.toContain("Execution");
|
||||
expect(text).not.toContain("Leave this unset to inherit the instance default");
|
||||
expect(text).not.toContain("Inherit instance default");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user