import { expect, test } from "../../fixtures/test-base"; import { useRegularMode } from "../../helpers/regular-mode"; import { KanbanPage } from "../../pages/kanban-page"; useRegularMode(); const DEPENDENCY_TRIGGER = "task-create-dependencies-trigger"; const DEPENDENCY_OPTION_PREFIX = "task-create-dependency-option-"; function taskIdFromUrl(url: string): string { const match = url.match(/\/t\/([^/?]+)/); if (!match) throw new Error(`Task route missing from ${url}`); return match[1]; } test.describe("Task-create dependency selector", () => { test("selects, clears, and persists multiple predecessor tasks", async ({ testPage, apiClient, seedData, }) => { const extraWorkflow = await apiClient.createWorkflow( seedData.workspaceId, "Dependency selector workflow", "simple", ); const taskOptions = { workflow_id: seedData.workflowId, workflow_step_id: seedData.startStepId, repository_ids: [seedData.repositoryId], }; const alpha = await apiClient.createTask(seedData.workspaceId, "Dependency Alpha", taskOptions); const beta = await apiClient.createTask(seedData.workspaceId, "Dependency Beta", taskOptions); const archived = await apiClient.createTask( seedData.workspaceId, "Archived dependency", taskOptions, ); await apiClient.archiveTask(archived.id); try { const kanban = new KanbanPage(testPage); await kanban.goto(); await kanban.createTaskButton.first().click(); const dialog = testPage.getByTestId("create-task-dialog"); await expect(dialog).toBeVisible(); const advancedSettings = dialog.getByTestId("task-create-advanced-settings"); const advancedTrigger = advancedSettings.getByTestId("task-create-advanced-settings-trigger"); await expect(advancedTrigger).toBeVisible(); await expect(advancedTrigger).toHaveAttribute("aria-expanded", "false"); await expect(dialog.getByTestId(DEPENDENCY_TRIGGER)).toHaveCount(0); await expect(dialog.getByTestId("agent-profile-selector")).toBeVisible(); await expect(dialog.getByTestId("executor-profile-selector")).toBeVisible(); const workflowSelector = dialog.getByTestId("workflow-selector-trigger"); await expect(workflowSelector).toBeVisible(); expect( await advancedSettings.evaluate((element) => { const dialogRoot = element.closest('[data-testid="create-task-dialog"]'); if (!dialogRoot) return false; return [ "agent-profile-selector", "executor-profile-selector", "workflow-selector-trigger", ].every((testId) => { const control = dialogRoot.querySelector(`[data-testid="${testId}"]`); return ( control !== null && Boolean(control.compareDocumentPosition(element) & Node.DOCUMENT_POSITION_FOLLOWING) ); }); }), ).toBe(true); await advancedTrigger.click(); await expect(advancedTrigger).toHaveAttribute("aria-expanded", "true"); await expect( advancedSettings.getByTestId("task-create-dependency-setting-label"), ).toContainText("Depends on"); const settingGrid = advancedSettings.getByTestId("task-create-advanced-settings-grid"); const settingGridBox = await settingGrid.boundingBox(); const settingRow = advancedSettings.getByTestId("task-create-dependency-setting-row"); const settingRowBox = await settingRow.boundingBox(); const settingLabel = advancedSettings.getByTestId("task-create-dependency-setting-label"); const settingLabelBox = await settingLabel.boundingBox(); const selectorContainer = advancedSettings.getByTestId( "task-create-dependency-selector-container", ); const selectorContainerBox = await selectorContainer.boundingBox(); expect(settingGridBox).not.toBeNull(); expect(settingRowBox).not.toBeNull(); expect(settingLabelBox).not.toBeNull(); expect(selectorContainerBox).not.toBeNull(); expect(settingRowBox!.width).toBeLessThan(settingGridBox!.width * 0.75); expect(selectorContainerBox!.width).toBeLessThan(settingGridBox!.width * 0.75); expect(selectorContainerBox!.x).toBeGreaterThan(settingLabelBox!.x + settingLabelBox!.width); expect(Math.abs(selectorContainerBox!.y - settingLabelBox!.y)).toBeLessThanOrEqual(4); const settingInfo = advancedSettings.getByTestId("task-create-dependency-setting-info"); await settingInfo.hover(); await expect(testPage.locator('[data-slot="tooltip-content"]:visible').last()).toContainText( "This task waits until every selected task completes successfully.", ); const dependency = dialog.getByTestId(DEPENDENCY_TRIGGER); await expect(dependency).toBeVisible(); await expect(dependency).toContainText("No dependency"); await dependency.click(); const picker = dialog.getByTestId("task-create-dependencies-popover"); await expect(picker).toBeVisible(); const info = picker.getByTestId("task-create-dependency-info"); await expect(info).toBeVisible(); await info.hover(); await expect(testPage.locator('[data-slot="tooltip-content"]:visible').last()).toContainText( "This task waits until every selected task completes successfully.", ); const search = picker.getByPlaceholder("Search tasks..."); await search.fill("Dependency Alpha"); const alphaOption = picker.getByTestId(`${DEPENDENCY_OPTION_PREFIX}${alpha.id}`); await expect(alphaOption).toBeVisible(); await expect(alphaOption.getByTestId("task-create-dependency-task-icon")).toBeVisible(); await expect(picker.getByText("Archived dependency")).toHaveCount(0); await alphaOption.click(); await expect(dependency).toContainText("Dependency Alpha"); await search.fill("Dependency Beta"); const betaOption = picker.getByTestId(`${DEPENDENCY_OPTION_PREFIX}${beta.id}`); await expect(betaOption).toBeVisible(); await betaOption.click(); await expect(dependency).toContainText("2 dependencies"); await search.fill(""); await picker.getByTestId("task-create-no-dependency").click(); await expect(dependency).toContainText("No dependency"); await dependency.click(); await search.fill("Dependency Alpha"); await picker.getByTestId(`${DEPENDENCY_OPTION_PREFIX}${alpha.id}`).click(); await search.fill("Dependency Beta"); await picker.getByTestId(`${DEPENDENCY_OPTION_PREFIX}${beta.id}`).click(); await testPage.keyboard.press("Escape"); await dialog.getByTestId("task-title-input").fill("Task with two dependencies"); await dialog .getByTestId("task-description-input") .fill("Created from the dependency selector"); await expect(dialog.getByTestId("submit-start-agent")).toBeEnabled({ timeout: 30_000 }); await dialog.getByTestId("submit-start-agent-chevron").click(); await testPage.getByTestId("submit-create-without-agent").click(); await expect(dialog).not.toBeVisible({ timeout: 15_000 }); await expect(testPage).toHaveURL(/\/t\//, { timeout: 15_000 }); const createdTask = await apiClient.getTask(taskIdFromUrl(testPage.url())); const dependencies = await apiClient.getTaskDependencies(createdTask.id); expect(dependencies.depends_on).toHaveLength(2); expect(dependencies.depends_on?.map((dependencyRef) => dependencyRef.id)).toEqual( expect.arrayContaining([alpha.id, beta.id]), ); } finally { await apiClient.deleteWorkflow(extraWorkflow.id).catch(() => {}); } }); });