297 lines
11 KiB
TypeScript
297 lines
11 KiB
TypeScript
import { test, expect } from "../../fixtures/test-base";
|
|
import { WorkflowSettingsPage } from "../../pages/workflow-settings-page";
|
|
import { KanbanPage } from "../../pages/kanban-page";
|
|
|
|
test.describe("Workflow agent profile", () => {
|
|
test("set workflow-level agent profile in settings and persist after save", async ({
|
|
testPage,
|
|
seedData,
|
|
apiClient,
|
|
}) => {
|
|
const page = new WorkflowSettingsPage(testPage);
|
|
await page.goto(seedData.workspaceId);
|
|
|
|
// Find the seeded "E2E Workflow" card
|
|
const card = await page.findWorkflowCard("E2E Workflow");
|
|
await expect(card).toBeVisible();
|
|
|
|
// The "Default Agent Profile" select should initially show "None (use task default)"
|
|
const profileSelect = page.workflowAgentProfileSelect(card);
|
|
await expect(profileSelect).toBeVisible();
|
|
|
|
// Get the available agent profiles from the API so we know the label to select
|
|
const { agents } = await apiClient.listAgents();
|
|
const agentProfile = agents.flatMap((a) => a.profiles ?? [])[0];
|
|
expect(agentProfile).toBeDefined();
|
|
const profileLabel = `${agentProfile.agent_display_name} \u2022 ${agentProfile.name}`;
|
|
|
|
// Open the select and pick the agent profile
|
|
await profileSelect.click();
|
|
await testPage.getByRole("option", { name: profileLabel }).click();
|
|
|
|
// Save the workflow
|
|
await page.saveButton(card).click();
|
|
await testPage.waitForTimeout(1000);
|
|
|
|
// Reload and verify the selection persists
|
|
await page.goto(seedData.workspaceId);
|
|
const reloadedCard = await page.findWorkflowCard("E2E Workflow");
|
|
await expect(reloadedCard).toBeVisible();
|
|
const reloadedSelect = page.workflowAgentProfileSelect(reloadedCard);
|
|
await expect(reloadedSelect).toContainText(profileLabel);
|
|
});
|
|
|
|
test("set per-step agent profile override in settings", async ({
|
|
testPage,
|
|
seedData,
|
|
apiClient,
|
|
}) => {
|
|
const page = new WorkflowSettingsPage(testPage);
|
|
await page.goto(seedData.workspaceId);
|
|
|
|
// Find the seeded workflow card
|
|
const card = await page.findWorkflowCard("E2E Workflow");
|
|
await expect(card).toBeVisible();
|
|
|
|
// Click on the first step to open the config panel
|
|
const firstStepName = seedData.steps[0]?.name;
|
|
expect(firstStepName).toBeDefined();
|
|
const stepNode = page.stepNodeByName(card, firstStepName!);
|
|
await stepNode.click();
|
|
|
|
// The step config panel should be visible with the "Agent Profile Override" select
|
|
const stepProfileSelect = page.stepAgentProfileSelect(card);
|
|
await expect(stepProfileSelect).toBeVisible();
|
|
|
|
// Get agent profile info
|
|
const { agents } = await apiClient.listAgents();
|
|
const agentProfile = agents.flatMap((a) => a.profiles ?? [])[0];
|
|
expect(agentProfile).toBeDefined();
|
|
const profileLabel = `${agentProfile.agent_display_name} \u2022 ${agentProfile.name}`;
|
|
|
|
// Select an agent profile for this step
|
|
await stepProfileSelect.click();
|
|
await testPage.getByRole("option", { name: profileLabel }).click();
|
|
|
|
// Wait for debounced update to propagate
|
|
await testPage.waitForTimeout(600);
|
|
|
|
// Save the workflow
|
|
await page.saveButton(card).click();
|
|
await testPage.waitForTimeout(1000);
|
|
|
|
// Reload and verify - the step should now show the agent profile icon (IconUserCog)
|
|
await page.goto(seedData.workspaceId);
|
|
const reloadedCard = await page.findWorkflowCard("E2E Workflow");
|
|
await expect(reloadedCard).toBeVisible();
|
|
|
|
// The step node should have a UserCog icon indicating a custom agent profile
|
|
const reloadedStepNode = page.stepNodeByName(reloadedCard, firstStepName!);
|
|
await expect(reloadedStepNode.locator(".tabler-icon-user-cog")).toBeVisible();
|
|
});
|
|
|
|
test("task creation dialog locks agent selector when workflow has agent profile", async ({
|
|
testPage,
|
|
seedData,
|
|
apiClient,
|
|
}) => {
|
|
// Set an agent profile on the seeded workflow via API
|
|
const { agents } = await apiClient.listAgents();
|
|
const agentProfile = agents.flatMap((a) => a.profiles ?? [])[0];
|
|
expect(agentProfile).toBeDefined();
|
|
await apiClient.updateWorkflow(seedData.workflowId, {
|
|
agent_profile_id: agentProfile.id,
|
|
});
|
|
|
|
let noProfileWorkflowId: string | undefined;
|
|
try {
|
|
// Create a second workflow without an agent profile for comparison
|
|
const noProfileWorkflow = await apiClient.createWorkflow(
|
|
seedData.workspaceId,
|
|
"No Profile Workflow",
|
|
"simple",
|
|
);
|
|
noProfileWorkflowId = noProfileWorkflow.id;
|
|
|
|
// Open the kanban page — reload to pick up the updated workflow agent_profile_id
|
|
const kanban = new KanbanPage(testPage);
|
|
await kanban.goto();
|
|
await testPage.reload();
|
|
|
|
await kanban.createTaskButton.first().click();
|
|
const dialog = testPage.getByTestId("create-task-dialog");
|
|
await expect(dialog).toBeVisible();
|
|
|
|
// Fill title so the selectors become visible
|
|
await testPage.getByTestId("task-title-input").fill("Agent Lock Test");
|
|
await testPage.getByTestId("task-description-input").fill("testing agent lock");
|
|
|
|
// The seeded workflow should be selected by default (from user settings).
|
|
// The agent selector should be disabled because the workflow has an agent profile.
|
|
const agentSelector = testPage.getByTestId("agent-profile-selector");
|
|
await expect(agentSelector).toBeVisible({ timeout: 15_000 });
|
|
|
|
// "Agent set by workflow" text confirms the selector is locked — sufficient verification
|
|
await expect(testPage.getByText("Agent set by workflow")).toBeVisible({ timeout: 10_000 });
|
|
} finally {
|
|
// Always clean up, even if assertions fail
|
|
if (noProfileWorkflowId) {
|
|
await apiClient.deleteWorkflow(noProfileWorkflowId).catch(() => {});
|
|
}
|
|
await apiClient.updateWorkflow(seedData.workflowId, {
|
|
agent_profile_id: "",
|
|
});
|
|
}
|
|
});
|
|
|
|
test("single workflow with agent override enables Start task button and shows profile", async ({
|
|
testPage,
|
|
seedData,
|
|
apiClient,
|
|
}) => {
|
|
// Set an agent profile on the only workflow
|
|
const { agents } = await apiClient.listAgents();
|
|
const agentProfile = agents.flatMap((a) => a.profiles ?? [])[0];
|
|
expect(agentProfile).toBeDefined();
|
|
await apiClient.updateWorkflow(seedData.workflowId, {
|
|
agent_profile_id: agentProfile.id,
|
|
});
|
|
|
|
try {
|
|
// Verify the API actually has the agent_profile_id set
|
|
const { workflows: wfList } = await apiClient.listWorkflows(seedData.workspaceId);
|
|
const updatedWf = wfList.find((w) => w.id === seedData.workflowId);
|
|
expect(updatedWf?.agent_profile_id).toBe(agentProfile.id);
|
|
|
|
const kanban = new KanbanPage(testPage);
|
|
await kanban.goto();
|
|
// Reload to pick up the updated workflow; wait for network to settle
|
|
await testPage.reload({ waitUntil: "networkidle" });
|
|
|
|
await kanban.createTaskButton.first().click();
|
|
const dialog = testPage.getByTestId("create-task-dialog");
|
|
await expect(dialog).toBeVisible();
|
|
|
|
await testPage.getByTestId("task-title-input").fill("Single Workflow Test");
|
|
await testPage.getByTestId("task-description-input").fill("testing single workflow");
|
|
|
|
// The selector should be disabled with "Agent set by workflow" text
|
|
await expect(testPage.getByText("Agent set by workflow")).toBeVisible({ timeout: 10_000 });
|
|
|
|
// The agent selector should be disabled (locked by workflow)
|
|
const agentSelector = testPage.getByTestId("agent-profile-selector");
|
|
await expect(agentSelector).toBeDisabled({ timeout: 10_000 });
|
|
} finally {
|
|
await apiClient.updateWorkflow(seedData.workflowId, {
|
|
agent_profile_id: "",
|
|
});
|
|
}
|
|
});
|
|
|
|
test("workflow selector shows agent icon for workflow-level override", async ({
|
|
testPage,
|
|
seedData,
|
|
apiClient,
|
|
}) => {
|
|
const { agents } = await apiClient.listAgents();
|
|
const agentProfile = agents.flatMap((a) => a.profiles ?? [])[0];
|
|
expect(agentProfile).toBeDefined();
|
|
|
|
// Set agent profile on seeded workflow
|
|
await apiClient.updateWorkflow(seedData.workflowId, {
|
|
agent_profile_id: agentProfile.id,
|
|
});
|
|
|
|
let noProfileWorkflowId: string | undefined;
|
|
try {
|
|
// Create second workflow without agent profile so selector is visible
|
|
const noProfileWorkflow = await apiClient.createWorkflow(
|
|
seedData.workspaceId,
|
|
"Plain Workflow",
|
|
"simple",
|
|
);
|
|
noProfileWorkflowId = noProfileWorkflow.id;
|
|
|
|
const kanban = new KanbanPage(testPage);
|
|
await kanban.goto();
|
|
await testPage.reload({ waitUntil: "networkidle" });
|
|
|
|
await kanban.createTaskButton.first().click();
|
|
const dialog = testPage.getByTestId("create-task-dialog");
|
|
await expect(dialog).toBeVisible();
|
|
|
|
await testPage.getByTestId("task-title-input").fill("Icon Test");
|
|
|
|
// Open workflow selector
|
|
const workflowButton = dialog.locator("button", { hasText: "E2E Workflow" });
|
|
await expect(workflowButton).toBeVisible({ timeout: 10_000 });
|
|
await workflowButton.click();
|
|
|
|
// The workflow with agent override should have an agent logo
|
|
const agentLogo = testPage.getByTestId("workflow-agent-logo");
|
|
await expect(agentLogo.first()).toBeVisible();
|
|
} finally {
|
|
if (noProfileWorkflowId) {
|
|
await apiClient.deleteWorkflow(noProfileWorkflowId).catch(() => {});
|
|
}
|
|
await apiClient.updateWorkflow(seedData.workflowId, {
|
|
agent_profile_id: "",
|
|
});
|
|
}
|
|
});
|
|
|
|
test("step-level agent override shown in workflow selector", async ({
|
|
testPage,
|
|
seedData,
|
|
apiClient,
|
|
}) => {
|
|
const { agents } = await apiClient.listAgents();
|
|
const agentProfile = agents.flatMap((a) => a.profiles ?? [])[0];
|
|
expect(agentProfile).toBeDefined();
|
|
|
|
// Set agent profile on the first step
|
|
const firstStep = seedData.steps[0];
|
|
expect(firstStep).toBeDefined();
|
|
await apiClient.updateWorkflowStep(firstStep.id, {
|
|
agent_profile_id: agentProfile.id,
|
|
});
|
|
|
|
let extraWorkflowId: string | undefined;
|
|
try {
|
|
// Create second workflow so selector is visible
|
|
const extraWorkflow = await apiClient.createWorkflow(
|
|
seedData.workspaceId,
|
|
"Extra Workflow",
|
|
"simple",
|
|
);
|
|
extraWorkflowId = extraWorkflow.id;
|
|
|
|
const kanban = new KanbanPage(testPage);
|
|
await kanban.goto();
|
|
await testPage.reload({ waitUntil: "networkidle" });
|
|
|
|
await kanban.createTaskButton.first().click();
|
|
const dialog = testPage.getByTestId("create-task-dialog");
|
|
await expect(dialog).toBeVisible();
|
|
|
|
await testPage.getByTestId("task-title-input").fill("Step Override Test");
|
|
|
|
// Open workflow selector
|
|
const workflowButton = dialog.locator("button", { hasText: "E2E Workflow" });
|
|
await expect(workflowButton).toBeVisible({ timeout: 10_000 });
|
|
await workflowButton.click();
|
|
|
|
// The step with agent override should show an agent logo
|
|
const stepAgentLogo = testPage.getByTestId("step-agent-logo");
|
|
await expect(stepAgentLogo.first()).toBeVisible({ timeout: 10_000 });
|
|
} finally {
|
|
if (extraWorkflowId) {
|
|
await apiClient.deleteWorkflow(extraWorkflowId).catch(() => {});
|
|
}
|
|
await apiClient.updateWorkflowStep(firstStep.id, {
|
|
agent_profile_id: "",
|
|
});
|
|
}
|
|
});
|
|
});
|