Files
kandev/apps/web/e2e/tests/automations-settings.spec.ts
T

262 lines
11 KiB
TypeScript

import { test, expect } from "../fixtures/test-base";
import { AutomationsPage } from "../pages/automations-page";
test.describe("Automations settings page", () => {
test("list page shows empty state", async ({ testPage, seedData }) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
await automations.goto();
await expect(automations.emptyState).toBeVisible({ timeout: 10_000 });
await expect(automations.emptyState).toHaveText(/No automations yet/);
});
test("create scheduled automation via UI", async ({ testPage, seedData }) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
await automations.goto();
// Navigate to new automation form
await automations.newAutomationButton.click();
await expect(testPage).toHaveURL(/automations\/new/, { timeout: 10_000 });
await expect(automations.editor).toBeVisible();
// Fill in name
await automations.nameInput.fill("Daily Check");
// Select a schedule preset
await automations.schedulePreset("@daily").click();
// Select workflow and step
await automations.selectWorkflow("E2E Workflow");
await automations.selectWorkflowStep(seedData.steps[0].name);
// Save — button should be enabled now
await expect(automations.saveButton).toBeEnabled({ timeout: 5_000 });
await automations.saveButton.click();
// Should land on the listings page with the new automation visible
await expect(testPage).toHaveURL(/automations$/, { timeout: 15_000 });
await expect(automations.table).toBeVisible({ timeout: 10_000 });
await expect(testPage.getByText("Daily Check")).toBeVisible();
});
test("create automation with custom schedule expression", async ({ testPage, seedData }) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
await automations.gotoNew();
await automations.nameInput.fill("Custom Schedule");
await automations.customScheduleInput.fill("@every 2h");
await automations.customScheduleInput.blur();
// Select workflow and step
await automations.selectWorkflow("E2E Workflow");
await automations.selectWorkflowStep(seedData.steps[0].name);
await expect(automations.saveButton).toBeEnabled({ timeout: 5_000 });
await automations.saveButton.click();
await expect(testPage).toHaveURL(/automations$/, { timeout: 15_000 });
await expect(testPage.getByText("Custom Schedule")).toBeVisible({ timeout: 10_000 });
});
test("schedule validation rejects invalid expression", async ({ testPage, seedData }) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
await automations.gotoNew();
await automations.customScheduleInput.fill("invalid-cron");
await automations.customScheduleInput.blur();
// Should show error text
await expect(testPage.getByText("Invalid expression")).toBeVisible({ timeout: 5_000 });
});
test("edit automation name", async ({ testPage, seedData }) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
// Create an automation first
await automations.gotoNew();
await automations.nameInput.fill("Original Name");
await automations.schedulePreset("@hourly").click();
await automations.selectWorkflow("E2E Workflow");
await automations.selectWorkflowStep(seedData.steps[0].name);
await expect(automations.saveButton).toBeEnabled({ timeout: 5_000 });
await automations.saveButton.click();
// After create we land on the listings page — open the new automation
// by clicking its row. Wait for the table to render before locating
// the row so the click doesn't race the listings hydration.
await expect(testPage).toHaveURL(/automations$/, { timeout: 15_000 });
await expect(automations.table).toBeVisible({ timeout: 10_000 });
await automations.table.locator("tr", { hasText: "Original Name" }).click();
await expect(testPage).toHaveURL(/automations\/[a-f0-9-]+$/, { timeout: 10_000 });
await expect(automations.editor).toBeVisible();
// Edit the name
await automations.nameInput.clear();
await automations.nameInput.fill("Updated Name");
await automations.saveButton.click();
// Go back to list and verify
await automations.goto();
await expect(automations.table).toBeVisible({ timeout: 10_000 });
await expect(testPage.getByText("Updated Name")).toBeVisible();
});
test("delete automation from editor", async ({ testPage, seedData }) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
// Create an automation first
await automations.gotoNew();
await automations.nameInput.fill("To Be Deleted");
await automations.schedulePreset("@weekly").click();
await automations.selectWorkflow("E2E Workflow");
await automations.selectWorkflowStep(seedData.steps[0].name);
await expect(automations.saveButton).toBeEnabled({ timeout: 5_000 });
await automations.saveButton.click();
// Land on listings, click into the new row to reach the editor.
await expect(testPage).toHaveURL(/automations$/, { timeout: 15_000 });
await expect(automations.table).toBeVisible({ timeout: 10_000 });
await automations.table.locator("tr", { hasText: "To Be Deleted" }).click();
await expect(testPage).toHaveURL(/automations\/[a-f0-9-]+$/, { timeout: 10_000 });
// Delete it
await automations.deleteButton.click();
// Should redirect to list page
await expect(testPage).toHaveURL(/automations$/, { timeout: 10_000 });
// The deleted automation should not appear in the list
await expect(testPage.getByText("To Be Deleted")).not.toBeVisible({ timeout: 10_000 });
});
test("create webhook automation shows reveal dialog with URL and secret", async ({
testPage,
seedData,
}) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
await automations.gotoNew();
// Fill in name
await automations.nameInput.fill("My Webhook");
// Switch to webhook mode
await testPage.getByText("Or use a webhook instead").click();
// Select workflow and step
await automations.selectWorkflow("E2E Workflow");
await automations.selectWorkflowStep(seedData.steps[0].name);
// Save
await expect(automations.saveButton).toBeEnabled({ timeout: 5_000 });
await automations.saveButton.click();
// Dialog should appear with URL and secret
await expect(testPage.getByTestId("webhook-created-dialog")).toBeVisible({ timeout: 10_000 });
// Verify the webhook URL is shown in the dialog
await expect(testPage.locator('input[value*="/api/v1/automations/webhook/"]')).toBeVisible();
// Verify a non-empty secret input is shown
const secretInputs = testPage.locator("input[readonly]");
const count = await secretInputs.count();
let hasNonEmptySecret = false;
for (let i = 0; i < count; i++) {
const val = await secretInputs.nth(i).inputValue();
if (val && !val.includes("/api/v1/automations/webhook/")) {
hasNonEmptySecret = true;
break;
}
}
expect(hasNonEmptySecret).toBe(true);
// Close the dialog
await testPage.getByTestId("webhook-created-dialog-close").click();
// Should redirect to listings and show the new automation
await expect(testPage).toHaveURL(/automations$/, { timeout: 15_000 });
await expect(automations.table).toBeVisible({ timeout: 10_000 });
await expect(testPage.getByText("My Webhook")).toBeVisible();
});
test("webhook secret is masked by default on the edit page and revealable", async ({
testPage,
seedData,
}) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
await automations.gotoNew();
// Create a webhook automation
await automations.nameInput.fill("Reveal Me");
await testPage.getByText("Or use a webhook instead").click();
await automations.selectWorkflow("E2E Workflow");
await automations.selectWorkflowStep(seedData.steps[0].name);
await expect(automations.saveButton).toBeEnabled({ timeout: 5_000 });
await automations.saveButton.click();
// Close the dialog and wait for listings
await expect(testPage.getByTestId("webhook-created-dialog")).toBeVisible({ timeout: 10_000 });
await testPage.getByTestId("webhook-created-dialog-close").click();
await expect(testPage).toHaveURL(/automations$/, { timeout: 15_000 });
await expect(automations.table).toBeVisible({ timeout: 10_000 });
// Click into the automation row to open the editor
await automations.table.locator("tr", { hasText: "Reveal Me" }).click();
await expect(testPage).toHaveURL(/automations\/[a-f0-9-]+$/, { timeout: 10_000 });
await expect(automations.editor).toBeVisible();
// Expand the webhook trigger card by clicking the summary button
await testPage.locator("button", { hasText: "Webhook" }).click();
// Secret should be masked by default
const secretInput = testPage.getByTestId("automation-webhook-secret-input");
await expect(secretInput).toBeVisible({ timeout: 10_000 });
await expect(secretInput).toHaveValue(/^•+$/);
// Click reveal toggle — secret should be unmasked
await testPage.getByTestId("automation-webhook-secret-toggle").click();
await expect(secretInput).not.toHaveValue(/^•+$/);
});
test("repository defaults to none on a fresh automation", async ({ testPage, seedData }) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
await automations.gotoNew();
// The repository selector should show "None" by default
await expect(testPage.getByTestId("repository-selector")).toContainText(/None|no repository/i, {
timeout: 10_000,
});
});
test("enable/disable toggle on list page", async ({ testPage, seedData }) => {
const automations = new AutomationsPage(testPage, seedData.workspaceId);
// Create an automation — the new flow lands directly on the listings page.
await automations.gotoNew();
await automations.nameInput.fill("Toggle Test");
await automations.schedulePreset("@daily").click();
await automations.selectWorkflow("E2E Workflow");
await automations.selectWorkflowStep(seedData.steps[0].name);
await expect(automations.saveButton).toBeEnabled({ timeout: 5_000 });
await automations.saveButton.click();
await expect(testPage).toHaveURL(/automations$/, { timeout: 15_000 });
await expect(automations.table).toBeVisible({ timeout: 10_000 });
// Find the toggle — automations are enabled by default.
// The table row containing "Toggle Test" has a switch inside it.
const row = automations.table.locator("tr", { hasText: "Toggle Test" });
const toggle = row.locator('[role="switch"]');
await expect(toggle).toBeChecked();
// Disable it
await toggle.click();
await expect(toggle).not.toBeChecked();
// Reload and verify it persisted
await testPage.reload();
await expect(automations.table).toBeVisible({ timeout: 10_000 });
const rowAfterReload = automations.table.locator("tr", { hasText: "Toggle Test" });
const toggleAfterReload = rowAfterReload.locator('[role="switch"]');
await expect(toggleAfterReload).not.toBeChecked();
});
});