Files
app/frontend/tests/drafts.spec.ts
T
stroblmeandClaude Fable 5 949fc0bf7e Ask for the name in a dialog instead of a field that is always there
Create in both overviews now opens a dialog, so the toolbar is a search box
and one button. The specs and the capture script open it first.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H7LwYgJfpkbLCTeiAf8U4A
2026-08-16 19:08:22 +02:00

88 lines
3.0 KiB
TypeScript

import { expect, test } from "@playwright/test"
import { api, deleteAll } from "./utils/api"
/**
* Editing writes a draft; only publishing hands it to the engine. The two
* things worth proving here are that the engine ignores a draft until it is
* published, and that a second client cannot quietly overwrite the first.
*/
const flowName = `test_draft_${Date.now().toString(36)}`
test.use({ storageState: "playwright/.auth/user.json" })
test.describe.configure({ mode: "serial" })
test.afterAll(async ({ browser }) => {
await deleteAll(browser, [`/flows/${flowName}`])
})
test("a draft stays off the engine until it is published", async ({ page }) => {
await page.goto("/flows")
await page.getByTestId("new-flow").click()
await page.getByTestId("new-flow-name").fill(flowName)
await page.getByTestId("create-flow").click()
await page.waitForURL(`/flows/${flowName}`)
await page.getByTestId("add-node").click()
await page
.getByRole("option", { name: /function/i })
.first()
.click()
await expect(page.locator(".react-flow__node")).toHaveCount(1)
// Adding a node opens its panel, and the floating chrome steps aside for it.
await page.keyboard.press("Escape")
await expect(page.getByTestId("publish-flow")).toBeVisible()
// Nothing of this flow is loaded while it is only a draft.
await page.waitForTimeout(1500)
const before = await (await api(page, `/flows/${flowName}/state`)).json()
expect(before.nodes).toHaveLength(0)
await page.getByTestId("publish-flow").click()
await expect(page.getByTestId("publish-flow")).toBeHidden()
const after = await (await api(page, `/flows/${flowName}/state`)).json()
expect(after.nodes.length).toBeGreaterThan(0)
})
test("a save against a version someone else moved on from is refused", async ({
page,
}) => {
await page.goto(`/flows/${flowName}`)
await page.waitForSelector(".react-flow__node")
const detail = await (await api(page, `/flows/${flowName}`)).json()
// Stand in for a second client that saved first.
const first = await api(page, `/flows/${flowName}`, {
method: "PUT",
data: { ...detail.definition, title: "Theirs" },
})
expect(first.ok()).toBeTruthy()
const stale = await api(page, `/flows/${flowName}`, {
method: "PUT",
data: { ...detail.definition, title: "Mine" },
})
expect(stale.status()).toBe(409)
expect((await stale.json()).detail.current_version).toBe(
detail.definition.version + 1,
)
})
test("discarding a draft goes back to the published flow", async ({ page }) => {
await page.goto(`/flows/${flowName}`)
await page.waitForSelector(".react-flow__node")
await page.getByTestId("edit-flow").click()
await page.getByTestId("discard-draft").click()
await page.getByTestId("confirm-discard-draft").click()
await expect(page.getByTestId("publish-flow")).toBeHidden()
const detail = await (await api(page, `/flows/${flowName}`)).json()
expect(detail.has_draft).toBe(false)
expect(detail.definition.title).not.toBe("Theirs")
})