2026-04-03 18:41:31 -07:00
|
|
|
|
import { expect, test } from "@playwright/test";
|
|
|
|
|
|
|
|
|
|
|
|
import { installMockBridge } from "../helpers/bridge";
|
|
|
|
|
|
|
|
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
|
|
|
|
await installMockBridge(page);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
async function navigateToWorkflows(page: import("@playwright/test").Page) {
|
|
|
|
|
|
await page.goto("/");
|
|
|
|
|
|
await page.getByTestId("open-workflows-view").click();
|
2026-04-07 12:20:42 -07:00
|
|
|
|
await expect(page).toHaveURL(/#\/workflows$/);
|
2026-04-03 18:41:31 -07:00
|
|
|
|
await expect(page.getByTestId("workflows-view")).toBeVisible();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function createWorkflow(
|
|
|
|
|
|
page: import("@playwright/test").Page,
|
|
|
|
|
|
name: string,
|
2026-04-06 11:41:52 -07:00
|
|
|
|
options?: {
|
|
|
|
|
|
description?: string;
|
|
|
|
|
|
enabled?: boolean;
|
|
|
|
|
|
trigger?: string;
|
|
|
|
|
|
stepCondition?: string;
|
|
|
|
|
|
stepName?: string;
|
|
|
|
|
|
stepTimeoutSecs?: string;
|
|
|
|
|
|
},
|
2026-04-03 18:41:31 -07:00
|
|
|
|
) {
|
|
|
|
|
|
await page.getByRole("button", { name: "Create Workflow" }).click();
|
2026-04-06 11:41:52 -07:00
|
|
|
|
const dialog = page.getByRole("dialog");
|
|
|
|
|
|
await expect(dialog).toBeVisible();
|
2026-04-03 18:41:31 -07:00
|
|
|
|
|
2026-04-06 11:41:52 -07:00
|
|
|
|
await dialog.getByLabel("Workflow name").fill(name);
|
|
|
|
|
|
if (options?.description) {
|
|
|
|
|
|
await dialog.getByLabel("Description (optional)").fill(options.description);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options?.enabled === false) {
|
|
|
|
|
|
await dialog.getByLabel("Workflow is enabled").click();
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options?.trigger) {
|
|
|
|
|
|
await dialog.getByLabel("Trigger").selectOption(options.trigger);
|
|
|
|
|
|
}
|
2026-04-03 18:41:31 -07:00
|
|
|
|
|
2026-04-06 11:41:52 -07:00
|
|
|
|
await dialog.getByRole("button", { name: "Add step" }).click();
|
|
|
|
|
|
if (options?.stepName) {
|
|
|
|
|
|
await dialog.getByLabel("Step name (optional)").fill(options.stepName);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options?.stepCondition) {
|
|
|
|
|
|
await dialog
|
|
|
|
|
|
.getByLabel("Run condition (optional)")
|
|
|
|
|
|
.fill(options.stepCondition);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options?.stepTimeoutSecs) {
|
|
|
|
|
|
await dialog
|
|
|
|
|
|
.getByLabel("Timeout seconds (optional)")
|
|
|
|
|
|
.fill(options.stepTimeoutSecs);
|
|
|
|
|
|
}
|
2026-04-03 18:41:31 -07:00
|
|
|
|
|
2026-04-06 11:41:52 -07:00
|
|
|
|
await dialog.getByRole("button", { name: "Create" }).click();
|
2026-04-03 18:41:31 -07:00
|
|
|
|
|
2026-04-06 11:41:52 -07:00
|
|
|
|
await expect(
|
|
|
|
|
|
page.getByRole("heading", { name: "Create Workflow" }),
|
|
|
|
|
|
).not.toBeVisible();
|
2026-04-03 18:41:31 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-17 09:56:34 -07:00
|
|
|
|
test("navigates to workflows view and shows the empty create tile", async ({
|
|
|
|
|
|
page,
|
|
|
|
|
|
}) => {
|
2026-04-03 18:41:31 -07:00
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
|
2026-08-17 09:56:34 -07:00
|
|
|
|
await expect(page.getByTestId("new-workflow-card")).toBeVisible();
|
|
|
|
|
|
await expect(page.locator('[data-testid^="workflow-card-"]')).toHaveCount(0);
|
2026-04-03 18:41:31 -07:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test("creates a workflow via the form builder", async ({ page }) => {
|
|
|
|
|
|
const workflowName = `test_workflow_${Date.now()}`;
|
|
|
|
|
|
|
|
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
await createWorkflow(page, workflowName);
|
|
|
|
|
|
|
|
|
|
|
|
// Verify workflow appears in the list
|
|
|
|
|
|
await expect(page.getByTestId("workflows-view")).toContainText(workflowName);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-04-06 18:55:30 -07:00
|
|
|
|
test("disables autocapitalization in the workflow form", async ({ page }) => {
|
|
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
|
|
|
|
|
|
await page.getByRole("button", { name: "Create Workflow" }).click();
|
|
|
|
|
|
const dialog = page.getByRole("dialog");
|
|
|
|
|
|
|
|
|
|
|
|
await expect(dialog.getByLabel("Workflow name")).toHaveAttribute(
|
|
|
|
|
|
"autocapitalize",
|
|
|
|
|
|
"off",
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
await dialog.getByRole("button", { name: "Add step" }).click();
|
|
|
|
|
|
await expect(dialog.getByLabel("Step name (optional)")).toHaveAttribute(
|
|
|
|
|
|
"autocapitalize",
|
|
|
|
|
|
"off",
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-08-17 09:56:34 -07:00
|
|
|
|
test("captures workflow library across responsive viewports", async ({
|
|
|
|
|
|
page,
|
|
|
|
|
|
}) => {
|
|
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
await createWorkflow(page, "Notify reviewers when source files change", {
|
|
|
|
|
|
description: "Watches diff events for src/ changes",
|
|
|
|
|
|
enabled: false,
|
|
|
|
|
|
trigger: "diff_posted",
|
|
|
|
|
|
});
|
|
|
|
|
|
await createWorkflow(page, "Post the daily standup reminder to the team", {
|
|
|
|
|
|
description: "Keeps the team aligned every morning",
|
|
|
|
|
|
trigger: "schedule",
|
|
|
|
|
|
});
|
|
|
|
|
|
await createWorkflow(
|
|
|
|
|
|
page,
|
|
|
|
|
|
"Request approval before deploying to production",
|
|
|
|
|
|
{
|
|
|
|
|
|
description: "Requires a final review before release",
|
|
|
|
|
|
trigger: "reaction_added",
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
for (const viewport of [
|
|
|
|
|
|
{ width: 800, height: 720, name: "narrow" },
|
|
|
|
|
|
{ width: 1024, height: 720, name: "medium" },
|
|
|
|
|
|
{ width: 1280, height: 720, name: "wide" },
|
|
|
|
|
|
]) {
|
|
|
|
|
|
await page.setViewportSize(viewport);
|
|
|
|
|
|
await page.screenshot({
|
|
|
|
|
|
animations: "disabled",
|
|
|
|
|
|
path: `test-results/workflow-library-${viewport.name}.png`,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await page.setViewportSize({ width: 1280, height: 720 });
|
|
|
|
|
|
const firstCard = page.locator('[data-testid^="workflow-card-"]').first();
|
|
|
|
|
|
await firstCard.getByRole("button", { name: "Workflow actions" }).click();
|
|
|
|
|
|
await page.screenshot({
|
|
|
|
|
|
animations: "disabled",
|
|
|
|
|
|
path: "test-results/workflow-library-wide-actions.png",
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-04-06 11:41:52 -07:00
|
|
|
|
test("captures disabled diff workflows in the list UI", async ({ page }) => {
|
|
|
|
|
|
const workflowName = `diff_workflow_${Date.now()}`;
|
|
|
|
|
|
const description = "Watches diff events for src/ changes";
|
|
|
|
|
|
|
|
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
await createWorkflow(page, workflowName, {
|
|
|
|
|
|
description,
|
|
|
|
|
|
enabled: false,
|
|
|
|
|
|
trigger: "diff_posted",
|
|
|
|
|
|
stepName: "Notify reviewers",
|
|
|
|
|
|
stepCondition: 'str_contains(trigger_text, "src/")',
|
|
|
|
|
|
stepTimeoutSecs: "45",
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const card = page
|
|
|
|
|
|
.locator('[data-testid^="workflow-card-"]')
|
|
|
|
|
|
.filter({ hasText: workflowName })
|
|
|
|
|
|
.first();
|
2026-08-17 09:56:34 -07:00
|
|
|
|
await expect(card.getByText("Diff Posted", { exact: true })).toBeVisible();
|
|
|
|
|
|
await expect(card.locator("h3")).toHaveText(workflowName);
|
|
|
|
|
|
await expect(card.getByText(description, { exact: true })).toBeVisible();
|
2026-04-06 11:41:52 -07:00
|
|
|
|
await expect(card).toContainText("disabled");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-08-17 09:56:34 -07:00
|
|
|
|
test("enables and disables a workflow from its card menu", async ({ page }) => {
|
|
|
|
|
|
const workflowName = `toggle_workflow_${Date.now()}`;
|
|
|
|
|
|
|
|
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
await createWorkflow(page, workflowName);
|
|
|
|
|
|
|
|
|
|
|
|
const workflowCard = () =>
|
|
|
|
|
|
page
|
|
|
|
|
|
.locator('[data-testid^="workflow-card-"]')
|
|
|
|
|
|
.filter({ hasText: workflowName })
|
|
|
|
|
|
.first();
|
|
|
|
|
|
const workflowActions = () =>
|
|
|
|
|
|
workflowCard().getByRole("button", { name: "Workflow actions" });
|
|
|
|
|
|
|
|
|
|
|
|
const enableItem = page.getByRole("menuitemcheckbox", { name: "Enable" });
|
|
|
|
|
|
|
|
|
|
|
|
await page.getByRole("button", { name: `View ${workflowName}` }).click();
|
|
|
|
|
|
const detailPanel = page.getByTestId("workflow-detail-panel");
|
|
|
|
|
|
await expect(detailPanel).toBeVisible();
|
|
|
|
|
|
await expect(detailPanel.getByText("active", { exact: true })).toBeVisible();
|
|
|
|
|
|
|
|
|
|
|
|
await workflowActions().click();
|
|
|
|
|
|
await expect(enableItem).toHaveAttribute("aria-checked", "true");
|
|
|
|
|
|
await expect(enableItem.locator("button")).toHaveCount(0);
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
enableItem.getByTestId("workflow-enabled-switch-visual"),
|
|
|
|
|
|
).toHaveAttribute("aria-hidden", "true");
|
|
|
|
|
|
await enableItem.click();
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
workflowCard().getByText("disabled", { exact: true }),
|
|
|
|
|
|
).toBeVisible();
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
detailPanel.getByText("disabled", { exact: true }),
|
|
|
|
|
|
).toBeVisible();
|
|
|
|
|
|
|
|
|
|
|
|
await enableItem.click();
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
workflowCard().getByText("active", { exact: true }),
|
|
|
|
|
|
).toBeVisible();
|
|
|
|
|
|
await expect(detailPanel.getByText("active", { exact: true })).toBeVisible();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test("rejects a stale card toggle without overwriting a newer edit", async ({
|
|
|
|
|
|
page,
|
|
|
|
|
|
}) => {
|
|
|
|
|
|
const workflowName = `stale_toggle_${Date.now()}`;
|
|
|
|
|
|
|
|
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
await createWorkflow(page, workflowName);
|
|
|
|
|
|
const workflowCard = page
|
|
|
|
|
|
.locator('[data-testid^="workflow-card-"]')
|
|
|
|
|
|
.filter({ hasText: workflowName })
|
|
|
|
|
|
.first();
|
|
|
|
|
|
|
|
|
|
|
|
await page.evaluate(async (name) => {
|
|
|
|
|
|
const invoke = window.__BUZZ_E2E_INVOKE_MOCK_COMMAND__;
|
|
|
|
|
|
if (!invoke) throw new Error("mock command bridge unavailable");
|
|
|
|
|
|
const createCall = [...(window.__BUZZ_E2E_COMMAND_PAYLOADS__ ?? [])]
|
|
|
|
|
|
.reverse()
|
|
|
|
|
|
.find((call) => call.command === "create_workflow");
|
|
|
|
|
|
const channelId = (
|
|
|
|
|
|
createCall?.payload as { channelId?: string } | undefined
|
|
|
|
|
|
)?.channelId;
|
|
|
|
|
|
if (!channelId) throw new Error("create workflow channel unavailable");
|
|
|
|
|
|
const workflows = (await invoke("get_channels_workflows", {
|
|
|
|
|
|
channelIds: [channelId],
|
|
|
|
|
|
})) as Array<{
|
|
|
|
|
|
id: string;
|
|
|
|
|
|
revision: string;
|
|
|
|
|
|
definition: Record<string, unknown>;
|
|
|
|
|
|
}>;
|
|
|
|
|
|
const workflow = workflows.find(
|
|
|
|
|
|
(candidate) => candidate.definition.name === name,
|
|
|
|
|
|
);
|
|
|
|
|
|
if (!workflow) throw new Error("created workflow unavailable");
|
|
|
|
|
|
await invoke("update_workflow", {
|
|
|
|
|
|
workflowId: workflow.id,
|
|
|
|
|
|
expectedRevision: workflow.revision,
|
|
|
|
|
|
yamlDefinition: `name: ${name} edited elsewhere\nenabled: true\ntrigger:\n on: message_posted\nsteps:\n - id: step_1\n action: post_message\n`,
|
|
|
|
|
|
});
|
|
|
|
|
|
}, workflowName);
|
|
|
|
|
|
|
|
|
|
|
|
await workflowCard.getByRole("button", { name: "Workflow actions" }).click();
|
|
|
|
|
|
await page.getByRole("menuitemcheckbox", { name: "Enable" }).click();
|
|
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
page
|
|
|
|
|
|
.locator("[data-sonner-toast][data-removed='false']")
|
|
|
|
|
|
.filter({ hasText: "workflow changed since it was loaded" }),
|
|
|
|
|
|
).toBeVisible();
|
|
|
|
|
|
const authoritativeName = await page.evaluate(async () => {
|
|
|
|
|
|
const invoke = window.__BUZZ_E2E_INVOKE_MOCK_COMMAND__;
|
|
|
|
|
|
if (!invoke) throw new Error("mock command bridge unavailable");
|
|
|
|
|
|
const createCall = [...(window.__BUZZ_E2E_COMMAND_PAYLOADS__ ?? [])]
|
|
|
|
|
|
.reverse()
|
|
|
|
|
|
.find((call) => call.command === "create_workflow");
|
|
|
|
|
|
const channelId = (
|
|
|
|
|
|
createCall?.payload as { channelId?: string } | undefined
|
|
|
|
|
|
)?.channelId;
|
|
|
|
|
|
if (!channelId) throw new Error("create workflow channel unavailable");
|
|
|
|
|
|
const workflows = (await invoke("get_channels_workflows", {
|
|
|
|
|
|
channelIds: [channelId],
|
|
|
|
|
|
})) as Array<{ name: string }>;
|
|
|
|
|
|
return workflows[0]?.name;
|
|
|
|
|
|
});
|
|
|
|
|
|
expect(authoritativeName).toBe(`${workflowName} edited elsewhere`);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test("reports a rejected workflow status change", async ({ page }) => {
|
|
|
|
|
|
const workflowName = `rejected_toggle_${Date.now()}`;
|
|
|
|
|
|
|
|
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
await createWorkflow(page, workflowName);
|
|
|
|
|
|
await page.evaluate(() => {
|
|
|
|
|
|
window.__BUZZ_E2E__ ??= {};
|
|
|
|
|
|
window.__BUZZ_E2E__.mock ??= {};
|
|
|
|
|
|
window.__BUZZ_E2E__.mock.workflowUpdateError = "relay refused the update";
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const workflowCard = page
|
|
|
|
|
|
.locator('[data-testid^="workflow-card-"]')
|
|
|
|
|
|
.filter({ hasText: workflowName })
|
|
|
|
|
|
.first();
|
|
|
|
|
|
await workflowCard.getByRole("button", { name: "Workflow actions" }).click();
|
|
|
|
|
|
await page.getByRole("menuitemcheckbox", { name: "Enable" }).click();
|
|
|
|
|
|
|
|
|
|
|
|
const errorToast = page
|
|
|
|
|
|
.locator("[data-sonner-toast][data-removed='false']")
|
|
|
|
|
|
.filter({ hasText: "Couldn’t change workflow status" });
|
|
|
|
|
|
await expect(errorToast).toContainText("relay refused the update");
|
|
|
|
|
|
await expect(workflowCard.getByText("active", { exact: true })).toBeVisible();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-04-06 11:41:52 -07:00
|
|
|
|
test("shows the webhook secret dialog after saving a webhook workflow", async ({
|
|
|
|
|
|
page,
|
|
|
|
|
|
}) => {
|
|
|
|
|
|
const workflowName = `webhook_workflow_${Date.now()}`;
|
|
|
|
|
|
|
|
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
await createWorkflow(page, workflowName, {
|
|
|
|
|
|
trigger: "webhook",
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
await expect(page.getByText("Webhook Ready")).toBeVisible();
|
|
|
|
|
|
await expect(page.getByRole("button", { name: "Copy URL" })).toBeVisible();
|
|
|
|
|
|
await expect(page.getByRole("button", { name: "Copy Secret" })).toBeVisible();
|
|
|
|
|
|
|
|
|
|
|
|
await page.getByRole("button", { name: "Close" }).click();
|
|
|
|
|
|
await expect(page.getByText("Webhook Ready")).not.toBeVisible();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-04-03 18:41:31 -07:00
|
|
|
|
test("edits an existing workflow", async ({ page }) => {
|
|
|
|
|
|
const originalName = `edit_test_${Date.now()}`;
|
|
|
|
|
|
const updatedName = `${originalName}_updated`;
|
|
|
|
|
|
|
|
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
await createWorkflow(page, originalName);
|
|
|
|
|
|
|
|
|
|
|
|
// Verify it exists
|
|
|
|
|
|
await expect(page.getByTestId("workflows-view")).toContainText(originalName);
|
|
|
|
|
|
|
|
|
|
|
|
// Open the dropdown menu and click Edit
|
|
|
|
|
|
await page.getByRole("button", { name: "Workflow actions" }).first().click();
|
|
|
|
|
|
await page.getByRole("menuitem", { name: "Edit" }).click();
|
|
|
|
|
|
|
|
|
|
|
|
// Dialog should open in edit mode
|
|
|
|
|
|
await expect(page.getByRole("dialog")).toBeVisible();
|
|
|
|
|
|
await expect(page.getByText("Edit Workflow")).toBeVisible();
|
|
|
|
|
|
|
|
|
|
|
|
// Change the name
|
|
|
|
|
|
const nameInput = page.getByLabel("Workflow name");
|
|
|
|
|
|
await nameInput.clear();
|
|
|
|
|
|
await nameInput.fill(updatedName);
|
|
|
|
|
|
|
|
|
|
|
|
// Save
|
|
|
|
|
|
await page.getByRole("button", { name: "Save" }).click();
|
|
|
|
|
|
await expect(page.getByRole("dialog")).not.toBeVisible();
|
|
|
|
|
|
|
|
|
|
|
|
// Verify the updated name appears
|
|
|
|
|
|
await expect(page.getByTestId("workflows-view")).toContainText(updatedName);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test("duplicates a workflow", async ({ page }) => {
|
|
|
|
|
|
const originalName = `dup_test_${Date.now()}`;
|
|
|
|
|
|
|
|
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
await createWorkflow(page, originalName);
|
|
|
|
|
|
|
|
|
|
|
|
// Open the dropdown menu and click Duplicate
|
|
|
|
|
|
await page.getByRole("button", { name: "Workflow actions" }).first().click();
|
|
|
|
|
|
await page.getByRole("menuitem", { name: "Duplicate" }).click();
|
|
|
|
|
|
|
|
|
|
|
|
// Dialog should open in duplicate mode with "(copy)" suffix
|
|
|
|
|
|
await expect(page.getByRole("dialog")).toBeVisible();
|
|
|
|
|
|
await expect(page.getByText("Duplicate Workflow")).toBeVisible();
|
|
|
|
|
|
|
|
|
|
|
|
// Submit the duplicate
|
|
|
|
|
|
await page.getByRole("button", { name: "Create Copy" }).click();
|
|
|
|
|
|
await expect(page.getByRole("dialog")).not.toBeVisible();
|
|
|
|
|
|
|
|
|
|
|
|
// Both the original and copy should exist
|
|
|
|
|
|
await expect(page.getByTestId("workflows-view")).toContainText(originalName);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test("deletes a workflow with confirmation", async ({ page }) => {
|
|
|
|
|
|
const workflowName = `delete_test_${Date.now()}`;
|
|
|
|
|
|
|
|
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
await createWorkflow(page, workflowName);
|
|
|
|
|
|
|
|
|
|
|
|
// Verify it exists
|
|
|
|
|
|
await expect(page.getByTestId("workflows-view")).toContainText(workflowName);
|
|
|
|
|
|
|
|
|
|
|
|
// Open the dropdown menu and click Delete
|
|
|
|
|
|
await page.getByRole("button", { name: "Workflow actions" }).first().click();
|
|
|
|
|
|
await page.getByRole("menuitem", { name: "Delete" }).click();
|
|
|
|
|
|
|
|
|
|
|
|
// Confirmation dialog should appear with workflow name
|
|
|
|
|
|
await expect(page.getByRole("alertdialog")).toBeVisible();
|
|
|
|
|
|
await expect(page.getByRole("alertdialog")).toContainText(workflowName);
|
|
|
|
|
|
|
|
|
|
|
|
// Confirm deletion
|
|
|
|
|
|
await page.getByRole("button", { name: "Delete" }).click();
|
|
|
|
|
|
await expect(page.getByRole("alertdialog")).not.toBeVisible();
|
|
|
|
|
|
|
2026-08-17 09:56:34 -07:00
|
|
|
|
// Verify workflow is gone — back to the empty create tile.
|
|
|
|
|
|
await expect(page.getByTestId("new-workflow-card")).toBeVisible();
|
|
|
|
|
|
await expect(page.locator('[data-testid^="workflow-card-"]')).toHaveCount(0);
|
2026-04-03 18:41:31 -07:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test("triggers a workflow from the detail panel", async ({ page }) => {
|
|
|
|
|
|
const workflowName = `trigger_test_${Date.now()}`;
|
|
|
|
|
|
|
|
|
|
|
|
await navigateToWorkflows(page);
|
|
|
|
|
|
await createWorkflow(page, workflowName);
|
|
|
|
|
|
|
|
|
|
|
|
// Click on the workflow card to open the detail panel
|
|
|
|
|
|
await page.getByRole("button", { name: `View ${workflowName}` }).click();
|
|
|
|
|
|
await expect(page.getByTestId("workflow-detail-panel")).toBeVisible();
|
|
|
|
|
|
|
|
|
|
|
|
// Click the Trigger button
|
|
|
|
|
|
await page
|
|
|
|
|
|
.getByTestId("workflow-detail-panel")
|
|
|
|
|
|
.getByRole("button", { name: "Trigger" })
|
|
|
|
|
|
.click();
|
|
|
|
|
|
|
|
|
|
|
|
// Wait for the trigger to complete (button text changes back from "Triggering...")
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
page
|
|
|
|
|
|
.getByTestId("workflow-detail-panel")
|
|
|
|
|
|
.getByRole("button", { name: "Trigger" }),
|
|
|
|
|
|
).toBeVisible();
|
2026-04-06 18:55:30 -07:00
|
|
|
|
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
page
|
|
|
|
|
|
.getByTestId("workflow-detail-panel")
|
|
|
|
|
|
.getByTestId("workflow-selected-run"),
|
|
|
|
|
|
).toBeVisible();
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
page.getByTestId("workflow-detail-panel").getByTestId("workflow-run-trace"),
|
|
|
|
|
|
).toContainText("step_1");
|
2026-04-03 18:41:31 -07:00
|
|
|
|
});
|