96 lines
3.4 KiB
TypeScript
96 lines
3.4 KiB
TypeScript
import { expect, test } from "@playwright/test"
|
|
import { api, deleteAll, openedFlowId } 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)}`
|
|
/** Issued by the server when the first test creates it in the app. */
|
|
let flowId = ""
|
|
|
|
test.use({ storageState: "playwright/.auth/user.json" })
|
|
|
|
test.describe.configure({ mode: "serial" })
|
|
|
|
test.afterAll(async ({ browser }) => {
|
|
await deleteAll(browser, [`/flows/${flowId}`])
|
|
})
|
|
|
|
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()
|
|
flowId = await openedFlowId(page)
|
|
|
|
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")
|
|
|
|
// The publish glyph never moves; it is enabled only while there is
|
|
// something to put live.
|
|
await expect(page.getByTestId("publish-flow")).toBeEnabled()
|
|
|
|
// Nothing of this flow is loaded while it is only a draft.
|
|
await page.waitForTimeout(1500)
|
|
const before = await (await api(page, `/flows/${flowId}/state`)).json()
|
|
expect(before.nodes).toHaveLength(0)
|
|
|
|
await page.getByTestId("publish-flow").click()
|
|
// Discard is what marks a draft: it goes when the publish has landed. The
|
|
// publish glyph disables while the request is in flight too, so waiting on
|
|
// that would not wait for anything.
|
|
await expect(page.getByTestId("discard-draft")).toBeHidden()
|
|
await expect(page.getByTestId("publish-flow")).toBeDisabled()
|
|
|
|
const after = await (await api(page, `/flows/${flowId}/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/${flowId}`)
|
|
await page.waitForSelector(".react-flow__node")
|
|
|
|
const detail = await (await api(page, `/flows/${flowId}`)).json()
|
|
|
|
// Stand in for a second client that saved first.
|
|
const first = await api(page, `/flows/${flowId}`, {
|
|
method: "PUT",
|
|
data: { ...detail.definition, title: "Theirs" },
|
|
})
|
|
expect(first.ok()).toBeTruthy()
|
|
|
|
const stale = await api(page, `/flows/${flowId}`, {
|
|
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/${flowId}`)
|
|
await page.waitForSelector(".react-flow__node")
|
|
|
|
await page.getByTestId("discard-draft").click()
|
|
await page.getByTestId("confirm-discard-draft").click()
|
|
|
|
await expect(page.getByTestId("discard-draft")).toBeHidden()
|
|
await expect(page.getByTestId("publish-flow")).toBeDisabled()
|
|
const detail = await (await api(page, `/flows/${flowId}`)).json()
|
|
expect(detail.has_draft).toBe(false)
|
|
expect(detail.definition.title).not.toBe("Theirs")
|
|
})
|