import { expect, test } from "@playwright/test" import { api, apiPage, deleteAll } from "./utils/api" /** * Flows can be taken off the engine and put back, and what a node prints — or * the traceback of one that fails — is readable without leaving the canvas. */ const flowName = `test_runtime_${Date.now().toString(36)}` test.use({ storageState: "playwright/.auth/user.json" }) test.describe.configure({ mode: "serial" }) const PRINTING_NODE = `def process(): print("sensor read 21.5 degrees") return {"reading": 21.5} ` const BROKEN_NODE = `def process(reading): raise RuntimeError("downstream blew up") ` test.afterAll(async ({ browser }) => { await deleteAll(browser, [`/flows/${flowName}`]) }) test.beforeAll(async ({ browser }) => { const page = await apiPage(browser) await api(page, `/flows/${flowName}`, { method: "PUT", data: { name: flowName, title: "Runtime", nodes: [ { id: "sensor", type: "python", provides: [{ name: "reading", dtype: "float" }], }, { id: "logger", type: "python", requires: [{ name: "reading", dtype: "float" }], }, ], }, }) await api(page, `/flows/${flowName}/nodes/sensor/source`, { method: "PUT", data: { code: PRINTING_NODE }, }) await api(page, `/flows/${flowName}/nodes/logger/source`, { method: "PUT", data: { code: BROKEN_NODE }, }) const detail = await (await api(page, `/flows/${flowName}`)).json() await api(page, `/flows/${flowName}/publish`, { method: "POST", data: { version: detail.definition.version }, }) await page.close() }) test("the dashboard lists flows and can stop one", async ({ page }) => { await page.goto("/") const row = page .getByTestId("dashboard-flow-row") .filter({ hasText: "Runtime" }) await expect(row).toBeVisible() await expect(row).toContainText("Running") await row.getByTestId("flow-enabled-switch").click() await expect(row).toContainText("Stopped") // A stopped flow is not something the engine will run. const refused = await api(page, `/flows/${flowName}/run`, { method: "POST", data: { inputs: {} }, }) expect(refused.status()).toBe(409) await row.getByTestId("flow-enabled-switch").click() await expect(row).toContainText("Running") }) test("the logs panel shows what a node printed and why one failed", async ({ page, }) => { await page.goto(`/flows/${flowName}`) await page.waitForSelector(".react-flow__node") await page.getByTestId("run-flow").click() await page.getByTestId("flow-logs").click() const panel = page.locator('[data-slot="popover-content"]') await expect(panel).toContainText("sensor read 21.5 degrees") await expect(panel).toContainText("RuntimeError") }) test("a flow can be paused and let go again", async ({ page }) => { await page.goto(`/flows/${flowName}`) await page.waitForSelector(".react-flow__node") await page.getByTestId("pause-flow").click() await expect(page.getByTestId("resume-flow")).toBeVisible() expect((await (await api(page, `/flows/${flowName}`)).json()).paused).toBe( true, ) await page.getByTestId("resume-flow").click() await expect(page.getByTestId("pause-flow")).toBeVisible() })