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-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") })