A python node's settings are constants of its own function, so they are passed the way its ports are: by name. The controller binds them to the compiled function, the `params` field is gone from the worker and remote protocols, and a setting sharing a port's name is reported as a node error rather than shadowing it. The panel's scaffold follows suit and keeps the header in step with both ports and settings. The demo's `pace` moves from a flow input to a setting of the training node, which is what it always was. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUb8YpL2s3gmN9WTACTt4q
113 lines
3.2 KiB
TypeScript
113 lines
3.2 KiB
TypeScript
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()
|
|
})
|