175 lines
7.4 KiB
TypeScript
175 lines
7.4 KiB
TypeScript
import { test, expect } from "../../fixtures/test-base";
|
|
|
|
/**
|
|
* Verifies the unified CLI-flags feature on agent profiles:
|
|
*
|
|
* - The profile editor renders a "Agent CLI flags" section.
|
|
* - Users can add a custom flag via the form, save, and reload; the flag
|
|
* persists in the DB (assert via the PATCH round-trip).
|
|
* - Toggling a flag off persists across save/reload.
|
|
* - Removing a flag persists across save/reload.
|
|
*
|
|
* Full argv-assembly (tokenisation + launch path) is covered by
|
|
* backend unit tests in apps/backend/internal/agent/lifecycle — we don't
|
|
* exercise a full task launch here because that's both slow and redundant.
|
|
*/
|
|
test.describe("Agent profile — CLI flags", () => {
|
|
test("cli-flags section renders on profile editor", async ({ testPage, apiClient }) => {
|
|
test.setTimeout(60_000);
|
|
|
|
const { agents } = await apiClient.listAgents();
|
|
const agent = agents[0];
|
|
const profileId = agent.profiles[0].id;
|
|
|
|
await testPage.goto(`/settings/agents/${agent.name}/profiles/${profileId}`);
|
|
|
|
// The Agent CLI flags card is always rendered on the profile editor.
|
|
const field = testPage.getByTestId("custom-cli-flags-card");
|
|
await expect(field).toBeVisible({ timeout: 15_000 });
|
|
|
|
// Header and helper copy anchoring the section for the user.
|
|
await expect(field).toContainText(/Agent CLI flags/i);
|
|
await expect(field).toContainText(/Flags passed to the agent CLI/i);
|
|
|
|
// The Add form inputs and button are always available.
|
|
await expect(testPage.getByTestId("cli-flag-new-flag-input")).toBeVisible();
|
|
await expect(testPage.getByTestId("cli-flag-add-button")).toBeVisible();
|
|
});
|
|
|
|
test("adding a custom flag persists across reload", async ({ testPage, apiClient }) => {
|
|
test.setTimeout(90_000);
|
|
|
|
// Create a fresh profile so we don't pollute other tests' fixtures.
|
|
const { agents } = await apiClient.listAgents();
|
|
const agent = agents[0];
|
|
const profile = await apiClient.createAgentProfile(agent.id, "CLI flags test", {
|
|
model: "mock-fast",
|
|
});
|
|
|
|
try {
|
|
await testPage.goto(`/settings/agents/${agent.name}/profiles/${profile.id}`);
|
|
await expect(testPage.getByTestId("custom-cli-flags-card")).toBeVisible({ timeout: 15_000 });
|
|
|
|
// Add a custom flag via the UI.
|
|
const flagText = "--my-custom-flag=value";
|
|
await testPage.getByTestId("cli-flag-new-flag-input").fill(flagText);
|
|
await testPage.getByTestId("cli-flag-add-button").click();
|
|
|
|
// The row should appear immediately, enabled by default. Flag text lives
|
|
// in an <input> element; use toHaveValue on the last flag-input because
|
|
// the newly added flag is always appended last.
|
|
await expect(testPage.locator('[data-testid^="cli-flag-flag-"]').last()).toHaveValue(
|
|
flagText,
|
|
);
|
|
|
|
// Save via the dirty-state save button; wait for unsaved badge to clear.
|
|
const saveButton = testPage.getByRole("button", { name: /^Save( changes)?$/i }).first();
|
|
await expect(saveButton).toBeEnabled({ timeout: 10_000 });
|
|
await saveButton.click();
|
|
await expect(testPage.getByText(/unsaved changes/i)).toBeHidden({ timeout: 15_000 });
|
|
|
|
// Reload — the row must still be there.
|
|
await testPage.reload();
|
|
await expect(testPage.getByTestId("custom-cli-flags-card")).toBeVisible({ timeout: 15_000 });
|
|
await expect(testPage.locator('[data-testid^="cli-flag-flag-"]').last()).toHaveValue(
|
|
flagText,
|
|
);
|
|
|
|
// Direct DB-path verification: fetch the profile via the API and assert
|
|
// the normalized CLI flags contain our entry, enabled.
|
|
const stored = await apiClient.getAgentProfile(profile.id);
|
|
const found = stored.cliFlags.find((f) => f.flag === flagText);
|
|
expect(found, `cliFlags should include ${flagText}`).toBeDefined();
|
|
expect(found?.enabled).toBe(true);
|
|
} finally {
|
|
await apiClient.deleteAgentProfile(profile.id, true);
|
|
}
|
|
});
|
|
|
|
test("toggling a flag off persists across reload", async ({ testPage, apiClient }) => {
|
|
test.setTimeout(90_000);
|
|
|
|
const { agents } = await apiClient.listAgents();
|
|
const agent = agents[0];
|
|
|
|
// Seed a profile with one enabled flag via the API so the UI has
|
|
// something to toggle off.
|
|
const profile = await apiClient.createAgentProfile(agent.id, "CLI flags toggle test", {
|
|
model: "mock-fast",
|
|
cli_flags: [{ description: "toggle target", flag: "--toggle-me", enabled: true }],
|
|
});
|
|
|
|
try {
|
|
await testPage.goto(`/settings/agents/${agent.name}/profiles/${profile.id}`);
|
|
await expect(testPage.getByTestId("custom-cli-flags-card")).toBeVisible({ timeout: 15_000 });
|
|
|
|
// The first row should be enabled; toggle it off.
|
|
const toggle = testPage.getByTestId("cli-flag-enabled-0");
|
|
await expect(toggle).toHaveAttribute("data-state", "checked");
|
|
await toggle.click();
|
|
await expect(toggle).toHaveAttribute("data-state", "unchecked");
|
|
|
|
const saveButton = testPage.getByRole("button", { name: /^Save( changes)?$/i }).first();
|
|
await expect(saveButton).toBeEnabled({ timeout: 10_000 });
|
|
await saveButton.click();
|
|
await expect(testPage.getByText(/unsaved changes/i)).toBeHidden({ timeout: 15_000 });
|
|
|
|
// Reload and confirm the toggle remained off.
|
|
await testPage.reload();
|
|
await expect(testPage.getByTestId("cli-flag-enabled-0")).toHaveAttribute(
|
|
"data-state",
|
|
"unchecked",
|
|
{ timeout: 15_000 },
|
|
);
|
|
|
|
const stored = await apiClient.getAgentProfile(profile.id);
|
|
const found = stored.cliFlags.find((f) => f.flag === "--toggle-me");
|
|
expect(found?.enabled).toBe(false);
|
|
} finally {
|
|
await apiClient.deleteAgentProfile(profile.id, true);
|
|
}
|
|
});
|
|
|
|
test("removing a flag persists across reload", async ({ testPage, apiClient }) => {
|
|
test.setTimeout(90_000);
|
|
|
|
const { agents } = await apiClient.listAgents();
|
|
const agent = agents[0];
|
|
|
|
const profile = await apiClient.createAgentProfile(agent.id, "CLI flags remove test", {
|
|
model: "mock-fast",
|
|
cli_flags: [
|
|
{ description: "keep", flag: "--keep-me", enabled: true },
|
|
{ description: "remove", flag: "--remove-me", enabled: true },
|
|
],
|
|
});
|
|
|
|
try {
|
|
await testPage.goto(`/settings/agents/${agent.name}/profiles/${profile.id}`);
|
|
await expect(testPage.getByTestId("custom-cli-flags-card")).toBeVisible({ timeout: 15_000 });
|
|
|
|
// Two rows present initially.
|
|
await expect(testPage.getByTestId("cli-flag-row-0")).toBeVisible();
|
|
await expect(testPage.getByTestId("cli-flag-row-1")).toBeVisible();
|
|
|
|
// Remove the second (--remove-me).
|
|
await testPage.getByTestId("cli-flag-remove-1").click();
|
|
await expect(testPage.getByTestId("cli-flag-row-1")).toBeHidden();
|
|
|
|
const saveButton = testPage.getByRole("button", { name: /^Save( changes)?$/i }).first();
|
|
await expect(saveButton).toBeEnabled({ timeout: 10_000 });
|
|
await saveButton.click();
|
|
await expect(testPage.getByText(/unsaved changes/i)).toBeHidden({ timeout: 15_000 });
|
|
|
|
await testPage.reload();
|
|
await expect(testPage.getByTestId("cli-flag-row-0")).toBeVisible({ timeout: 15_000 });
|
|
await expect(testPage.getByTestId("cli-flag-row-1")).toBeHidden();
|
|
|
|
const stored = await apiClient.getAgentProfile(profile.id);
|
|
expect(stored.cliFlags.map((f) => f.flag)).toEqual(["--keep-me"]);
|
|
} finally {
|
|
await apiClient.deleteAgentProfile(profile.id, true);
|
|
}
|
|
});
|
|
});
|