diff --git a/src/bundled/workflows/WorkflowChannel.test.tsx b/src/bundled/workflows/WorkflowChannel.test.tsx
new file mode 100644
index 00000000..a2a44b14
--- /dev/null
+++ b/src/bundled/workflows/WorkflowChannel.test.tsx
@@ -0,0 +1,315 @@
+// @vitest-environment jsdom
+import "@testing-library/jest-dom/vitest";
+import { StrictMode } from "react";
+import {
+ act,
+ cleanup,
+ fireEvent,
+ render,
+ screen,
+ within,
+} from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { parse as parseYaml } from "yaml";
+import { afterEach, expect, it } from "vitest";
+import { WorkflowChannel } from "./WorkflowChannel";
+import {
+ createWorkflowFixture,
+ fixtureChannel,
+ fixtureCursor,
+ fixtureViewer,
+} from "./fixtures";
+
+afterEach(cleanup);
+
+function mount() {
+ const fixture = createWorkflowFixture();
+ const user = userEvent.setup();
+ render(
+
+
+ ,
+ );
+ return { fixture, user };
+}
+
+async function open(user: ReturnType) {
+ await user.click(screen.getByRole("button", { name: "Message helper" }));
+}
+
+const button = (name: string) => screen.getByRole("button", { name });
+const nameInput = () => screen.getByRole("textbox", { name: "Workflow name" });
+
+it("loads paged history lazily and acknowledges an unknown run without replay", async () => {
+ const { fixture, user } = mount();
+ await open(user);
+ expect(fixture.calls.runs).toBe(0);
+ await user.click(button("Read runs"));
+ expect(await screen.findByText("Current step: 1")).toBeVisible();
+ await user.click(button("Older runs"));
+ expect(
+ await screen.findByText("No runs returned on this page."),
+ ).toBeVisible();
+ expect(fixture.runCursor()).toEqual(fixtureCursor);
+ expect(fixture.runViews.slice(0, -1).every((view) => view.disposed())).toBe(
+ true,
+ );
+ await user.click(button("Hide runs"));
+ expect(fixture.runViews.every((view) => view.disposed())).toBe(true);
+
+ await user.click(button("Run now"));
+ act(() => fixture.finish("unknown"));
+ const id = fixture.capability.operations.snapshot().at(-1)?.eventId;
+ expect(button("Run now")).toBeDisabled();
+ expect(screen.getByText(/The run may have started/)).toBeVisible();
+ await user.click(button("Close editor"));
+ await open(user);
+ expect(button("Run now")).toBeDisabled();
+ expect(button("Save workflow")).toBeDisabled();
+ expect(fixture.calls.trigger).toBe(1);
+ await user.click(button("Dismiss notice"));
+ expect(screen.getByRole("alertdialog")).toHaveTextContent(
+ "does not undo, cancel or repeat",
+ );
+ await user.click(button("Dismiss notice and continue"));
+ expect(button("Run now")).toBeEnabled();
+ expect(button("Save workflow")).toBeEnabled();
+ expect(fixture.calls.dismiss).toEqual([id]);
+ expect(fixture.calls.trigger).toBe(1);
+});
+
+it("adopts an exact lost-save readback without resubmitting", async () => {
+ const { fixture, user } = mount();
+ await open(user);
+ await user.clear(nameInput());
+ await user.type(nameInput(), "Saved without response");
+ await user.click(button("Save workflow"));
+ act(() => {
+ fixture.saveOnServer();
+ fixture.finish("unknown");
+ });
+ expect(button("Save workflow")).toBeDisabled();
+ await user.click(button("Check saved configuration"));
+ expect(button("Save workflow")).toBeEnabled();
+ expect(nameInput()).toHaveValue("Saved without response");
+ expect(fixture.calls.save).toBe(1);
+ await user.type(
+ screen.getByRole("textbox", { name: "Message text" }),
+ " edit",
+ );
+ await user.click(button("Save workflow"));
+ expect(fixture.calls.save).toBe(2);
+});
+
+it("requires explicit different-head recovery and restores a failed dismissal", async () => {
+ const { fixture, user } = mount();
+ await open(user);
+ await user.clear(nameInput());
+ await user.type(nameInput(), "Retained local draft");
+ await user.click(button("Save workflow"));
+ act(() => {
+ fixture.saveOnServer(false);
+ fixture.finish("unknown");
+ });
+ await user.click(button("Check saved configuration"));
+ expect(button("Save workflow")).toBeDisabled();
+ expect(button("Review current configuration")).toBeVisible();
+ await user.click(button("Dismiss notice"));
+ await user.keyboard("{Escape}");
+ expect(fixture.calls.dismiss).toEqual([]);
+ expect(button("Save workflow")).toBeDisabled();
+
+ fixture.setDismissError("Fixture dismissal failed");
+ await user.click(button("Dismiss notice"));
+ await user.click(button("Dismiss notice and continue"));
+ expect(await screen.findByRole("alert")).toHaveTextContent(
+ "Fixture dismissal failed",
+ );
+ await user.keyboard("{Escape}");
+ expect(button("Save workflow")).toBeDisabled();
+ fixture.setDismissError();
+ await user.click(button("Dismiss notice"));
+ await user.click(button("Dismiss notice and continue"));
+ expect(button("Save workflow")).toBeEnabled();
+ expect(nameInput()).toHaveValue("Retained local draft");
+ expect(fixture.calls.save).toBe(1);
+ await user.click(button("Save workflow"));
+ expect(fixture.calls.save).toBe(2);
+});
+
+it("keeps optimistic dismissal modal and ownership locked until settlement", async () => {
+ const { fixture, user } = mount();
+ await open(user);
+ await user.clear(nameInput());
+ await user.type(nameInput(), "Kept draft");
+ await user.click(button("Save workflow"));
+ act(() => fixture.finish("unknown"));
+ const operationId = fixture.capability.operations.snapshot()[0]?.eventId;
+
+ for (const failure of ["Journal unavailable", undefined]) {
+ fixture.setDismissError(failure);
+ fixture.holdDismiss();
+ await user.click(button("Dismiss notice"));
+ const dialog = screen.getByRole("alertdialog", {
+ name: "Dismiss this notice?",
+ });
+ try {
+ await user.click(
+ within(dialog).getByRole("button", {
+ name: "Dismiss notice and continue",
+ }),
+ );
+ expect(fixture.capability.operations.snapshot()).toHaveLength(0);
+ expect(dialog).toBeVisible();
+ expect(
+ within(dialog).getByRole("button", { name: "Dismissing…" }),
+ ).toBeDisabled();
+ expect(
+ within(dialog).getByRole("button", { name: "Keep editing" }),
+ ).toBeDisabled();
+ await user.keyboard("{Escape}");
+ expect(dialog).toBeVisible();
+ expect(
+ screen.queryByRole("button", { name: "Close editor" }),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByRole("button", { name: "New workflow" }),
+ ).not.toBeInTheDocument();
+ expect(fixture.calls.save).toBe(1);
+ } finally {
+ await act(async () => fixture.releaseDismiss());
+ }
+ if (failure) {
+ expect(await within(dialog).findByRole("alert")).toHaveTextContent(
+ failure,
+ );
+ expect(
+ fixture.capability.operations.snapshot().map((op) => op.eventId),
+ ).toEqual([operationId]);
+ await user.keyboard("{Escape}");
+ expect(button("Save workflow")).toBeDisabled();
+ expect(nameInput()).toHaveValue("Kept draft");
+ } else {
+ expect(screen.queryByRole("alertdialog")).not.toBeInTheDocument();
+ expect(button("Save workflow")).toBeEnabled();
+ expect(nameInput()).toHaveValue("Kept draft");
+ }
+ }
+ expect(fixture.calls.dismiss).toEqual([operationId, operationId]);
+ expect(fixture.calls.save).toBe(1);
+ await user.click(button("Save workflow"));
+ expect(fixture.calls.save).toBe(2);
+});
+
+it("treats legacy deletion as a request rather than verified removal", async () => {
+ const { fixture, user } = mount();
+ await open(user);
+ await user.click(button("Delete workflow"));
+ expect(screen.getByRole("alertdialog")).toHaveTextContent(
+ "does not confirm runtime deletion",
+ );
+ await user.click(button("Request deletion"));
+ act(() => fixture.finish("succeeded"));
+ expect(
+ screen.getByText(/Deletion request accepted\. The saved configuration/),
+ ).toBeVisible();
+ expect(button("Message helper")).toBeVisible();
+ await user.click(button("Dismiss notice"));
+ await user.click(button("Dismiss notice and continue"));
+ expect(button("Save workflow")).toBeEnabled();
+ expect(fixture.calls.delete).toBe(1);
+});
+
+it("retains invalid timeout boundaries in both modes and saves exact valid YAML", async () => {
+ const { fixture, user } = mount();
+ await open(user);
+ await user.click(screen.getByText("Step options", { exact: true }));
+ for (const input of ["oops", "0s", "1.5", "9007199254740992"]) {
+ const timeout = screen.getByRole("textbox", {
+ name: "Step timeout (optional)",
+ });
+ await user.clear(timeout);
+ await user.type(timeout, input);
+ expect(timeout).toHaveValue(input);
+ expect(button("Save workflow")).toBeDisabled();
+ expect(screen.getByText(/positive whole number/)).toHaveAttribute(
+ "role",
+ "status",
+ );
+ await user.click(screen.getByRole("tab", { name: "YAML" }));
+ const yaml = screen.getByRole("textbox", { name: "Workflow YAML" });
+ expect(
+ parseYaml((yaml as HTMLTextAreaElement).value).steps[0].timeout_secs,
+ ).toBe(input);
+ expect(button("Save workflow")).toBeDisabled();
+ await user.click(screen.getByRole("tab", { name: "Form" }));
+ await user.click(screen.getByText("Step options", { exact: true }));
+ expect(
+ screen.getByRole("textbox", { name: "Step timeout (optional)" }),
+ ).toHaveValue(input);
+ }
+ await user.click(button("Close editor"));
+ expect(
+ screen.getByRole("alertdialog", { name: "Leave this draft?" }),
+ ).toBeVisible();
+ await user.click(button("Keep editing"));
+ const timeout = screen.getByRole("textbox", {
+ name: "Step timeout (optional)",
+ });
+ expect(timeout).toHaveValue("9007199254740992");
+ await user.clear(timeout);
+ await user.type(timeout, "5m");
+ expect(button("Save workflow")).toBeEnabled();
+ await user.click(button("Save workflow"));
+ expect(fixture.calls.save).toBe(1);
+ expect(parseYaml(fixture.input()?.yaml ?? "").steps[0].timeout_secs).toBe(
+ 300,
+ );
+ act(() => fixture.finish("succeeded"));
+ expect(button("Save workflow")).toBeEnabled();
+ if (!timeout.isConnected)
+ await user.click(screen.getByText("Step options", { exact: true }));
+ const currentTimeout = screen.getByRole("textbox", {
+ name: "Step timeout (optional)",
+ });
+ await user.clear(currentTimeout);
+ await user.type(currentTimeout, " ");
+ await user.click(button("Save workflow"));
+ expect(fixture.calls.save).toBe(2);
+ expect(parseYaml(fixture.input()?.yaml ?? "").steps[0]).not.toHaveProperty(
+ "timeout_secs",
+ );
+});
+
+it("allocates unused IDs after a parsed ID beyond the safe integer boundary", async () => {
+ const { user } = mount();
+ await open(user);
+ await user.click(screen.getByRole("tab", { name: "YAML" }));
+ const yaml = screen.getByRole("textbox", { name: "Workflow YAML" });
+ const definition = parseYaml((yaml as HTMLTextAreaElement).value);
+ definition.steps[0].id = "step_9007199254740992";
+ fireEvent.change(yaml, { target: { value: JSON.stringify(definition) } });
+ await user.click(screen.getByRole("tab", { name: "Form" }));
+ await user.click(button("Add Send Message"));
+ const messages = screen.getAllByRole("textbox", { name: "Message text" });
+ expect(messages).toHaveLength(2);
+ const addedMessage = messages[1];
+ if (!addedMessage) throw new Error("Expected the added message field");
+ await user.type(addedMessage, "Another message");
+ await user.click(button("Add Delay"));
+ await user.click(screen.getByRole("tab", { name: "YAML" }));
+ expect(
+ parseYaml(
+ (
+ screen.getByRole("textbox", {
+ name: "Workflow YAML",
+ }) as HTMLTextAreaElement
+ ).value,
+ ).steps.map((step: { id: string }) => step.id),
+ ).toEqual(["step_9007199254740992", "step_1", "step_2"]);
+});
diff --git a/src/bundled/workflows/workflows.journey.mjs b/src/bundled/workflows/workflows.journey.mjs
index c7d7890c..0d4a9ca9 100644
--- a/src/bundled/workflows/workflows.journey.mjs
+++ b/src/bundled/workflows/workflows.journey.mjs
@@ -274,66 +274,6 @@ test("keyboard switches feed enabled-save confirmation and disabled readback", a
expect(errors).toEqual([]);
});
-test("history stays lazy and paged; acknowledging an unknown run never repeats it", async ({
- page,
-}) => {
- await page.goto(url);
- const button = (name) => page.getByRole("button", { name, exact: true });
- await button("Message helper").click();
- expect(await page.evaluate(() => window.workflowFixture.calls.runs)).toBe(0);
- await button("Read runs").click();
- await expect(page.getByText("Current step: 1")).toBeVisible();
- await button("Older runs").click();
- await expect(page.getByText("No runs returned on this page.")).toBeVisible();
- expect(await page.evaluate(() => window.workflowFixture.runCursor())).toEqual(
- {
- before: "2026-09-12T12:00:00.123456Z",
- beforeId: "77777777-7777-4777-8777-777777777777",
- },
- );
- expect(
- await page.evaluate(() =>
- window.workflowFixture.runViews
- .slice(0, -1)
- .every((view) => view.disposed()),
- ),
- ).toBe(true);
- await button("Hide runs").click();
- expect(
- await page.evaluate(() =>
- window.workflowFixture.runViews.every((view) => view.disposed()),
- ),
- ).toBe(true);
- await button("Run now").click();
- await button("Unknown operation").click();
- await expect(button("Run now")).toBeDisabled();
- await expect(page.getByText(/The run may have started/)).toBeVisible();
- const id = await page.evaluate(
- () =>
- window.workflowFixture.capability.operations.snapshot().at(-1).eventId,
- );
- await button("Close editor").click();
- await button("Message helper").click();
- await expect(button("Run now")).toBeDisabled();
- await expect(button("Save workflow")).toBeDisabled();
- expect(await page.evaluate(() => window.workflowFixture.calls.trigger)).toBe(
- 1,
- );
- await button("Dismiss notice").click();
- await expect(page.getByRole("alertdialog")).toContainText(
- "does not undo, cancel or repeat",
- );
- await button("Dismiss notice and continue").click();
- await expect(button("Run now")).toBeEnabled();
- await expect(button("Save workflow")).toBeEnabled();
- expect(
- await page.evaluate(() => window.workflowFixture.calls.dismiss),
- ).toEqual([id]);
- expect(await page.evaluate(() => window.workflowFixture.calls.trigger)).toBe(
- 1,
- );
-});
-
test("real session page under StrictMode fences community changes, warns for dirty channel navigation and purges access", async ({
page,
}) => {
@@ -405,267 +345,6 @@ test("real session page under StrictMode fences community changes, warns for dir
expect(errors).toEqual([]);
});
-test("a lost save response can be checked and adopted without resubmitting", async ({
- page,
-}) => {
- await page.goto(url);
- const button = (name) => page.getByRole("button", { name, exact: true });
- await button("Message helper").click();
- await page
- .getByLabel("Workflow name", { exact: true })
- .fill("Saved without response");
- await button("Save workflow").click();
- await page.evaluate(() => {
- window.workflowFixture.saveOnServer();
- window.workflowFixture.finish("unknown");
- });
- await expect(button("Save workflow")).toBeDisabled();
- await button("Check saved configuration").click();
- await expect(button("Save workflow")).toBeEnabled();
- await expect(page.getByLabel("Workflow name", { exact: true })).toHaveValue(
- "Saved without response",
- );
- expect(await page.evaluate(() => window.workflowFixture.calls.save)).toBe(1);
- await page
- .getByLabel("Message text", { exact: true })
- .fill("Edit after recovery");
- await button("Save workflow").click();
- await expect
- .poll(() => page.evaluate(() => window.workflowFixture.calls.save))
- .toBe(2);
-});
-
-test("different-head recovery needs explicit review; failed dismissal keeps the draft locked", async ({
- page,
-}) => {
- await page.goto(url);
- const button = (name) => page.getByRole("button", { name, exact: true });
- await button("Message helper").click();
- await page
- .getByLabel("Workflow name", { exact: true })
- .fill("Retained local draft");
- await button("Save workflow").click();
- await page.evaluate(() => {
- window.workflowFixture.saveOnServer(false);
- window.workflowFixture.finish("unknown");
- });
- await button("Check saved configuration").click();
- await expect(button("Save workflow")).toBeDisabled();
- await expect(button("Review current configuration")).toBeVisible();
- await button("Dismiss notice").click();
- await page.keyboard.press("Escape");
- await expect(button("Save workflow")).toBeDisabled();
- expect(
- await page.evaluate(() => window.workflowFixture.calls.dismiss),
- ).toEqual([]);
- await page.evaluate(() =>
- window.workflowFixture.setDismissError("Fixture dismissal failed"),
- );
- await button("Dismiss notice").click();
- await button("Dismiss notice and continue").click();
- await expect(page.getByRole("alert")).toHaveText("Fixture dismissal failed");
- await page.keyboard.press("Escape");
- await expect(button("Save workflow")).toBeDisabled();
- await page.evaluate(() => window.workflowFixture.setDismissError());
- await button("Dismiss notice").click();
- await button("Dismiss notice and continue").click();
- await expect(button("Save workflow")).toBeEnabled();
- await expect(page.getByLabel("Workflow name", { exact: true })).toHaveValue(
- "Retained local draft",
- );
- expect(await page.evaluate(() => window.workflowFixture.calls.save)).toBe(1);
- await button("Save workflow").click();
- await expect
- .poll(() => page.evaluate(() => window.workflowFixture.calls.save))
- .toBe(2);
-});
-
-test("optimistic dismissal keeps confirmation mounted until persistence settles", async ({
- page,
-}) => {
- await page.goto(url);
- const button = (name) => page.getByRole("button", { name, exact: true });
- await button("Message helper").click();
- await page.getByLabel("Workflow name", { exact: true }).fill("Kept draft");
- await button("Save workflow").click();
- await page.evaluate(() => window.workflowFixture.finish("unknown"));
- const operationId = await page.evaluate(
- () => window.workflowFixture.capability.operations.snapshot()[0].eventId,
- );
- const dialog = page.getByRole("alertdialog", {
- name: "Dismiss this notice?",
- });
- for (const fail of [true, false]) {
- await page.evaluate((fail) => {
- window.workflowFixture.setDismissError(
- fail ? "Journal unavailable" : undefined,
- );
- window.workflowFixture.holdDismiss();
- }, fail);
- await button("Dismiss notice").click();
- try {
- await button("Dismiss notice and continue").click();
- await expect
- .poll(() =>
- page.evaluate(
- () =>
- window.workflowFixture.capability.operations.snapshot().length,
- ),
- )
- .toBe(0);
- await expect(dialog).toBeVisible();
- await expect(button("Dismissing…")).toBeDisabled();
- await expect(button("Keep editing")).toBeDisabled();
- await page.keyboard.press("Escape");
- await expect(dialog).toBeVisible();
- // The modal must keep navigation/submission inaccessible during the gap.
- await expect(button("Close editor")).toHaveCount(0);
- await expect(button("New workflow")).toHaveCount(0);
- expect(await page.evaluate(() => window.workflowFixture.calls.save)).toBe(
- 1,
- );
- } finally {
- await page.evaluate(() => window.workflowFixture.releaseDismiss());
- }
- if (fail) {
- await expect(dialog.getByRole("alert")).toHaveText("Journal unavailable");
- await expect
- .poll(() =>
- page.evaluate(() =>
- window.workflowFixture.capability.operations
- .snapshot()
- .map((op) => op.eventId),
- ),
- )
- .toEqual([operationId]);
- await page.keyboard.press("Escape");
- await expect(button("Save workflow")).toBeDisabled();
- await expect(
- page.getByLabel("Workflow name", { exact: true }),
- ).toHaveValue("Kept draft");
- } else {
- await expect(dialog).toHaveCount(0);
- await expect(button("Save workflow")).toBeEnabled();
- await expect(
- page.getByLabel("Workflow name", { exact: true }),
- ).toHaveValue("Kept draft");
- }
- }
- expect(
- await page.evaluate(() => window.workflowFixture.calls.dismiss),
- ).toEqual([operationId, operationId]);
- expect(await page.evaluate(() => window.workflowFixture.calls.save)).toBe(1);
- await button("Save workflow").click();
- await expect
- .poll(() => page.evaluate(() => window.workflowFixture.calls.save))
- .toBe(2);
-});
-
-test("legacy deletion is a request, not verified runtime removal", async ({
- page,
-}) => {
- await page.goto(url);
- const button = (name) => page.getByRole("button", { name, exact: true });
- await button("Message helper").click();
- await button("Delete workflow").click();
- await expect(page.getByRole("alertdialog")).toContainText(
- "does not confirm runtime deletion",
- );
- await button("Request deletion").click();
- await page.evaluate(() => window.workflowFixture.finish("succeeded"));
- await expect(
- page.getByText(/Deletion request accepted\. The saved configuration/),
- ).toBeVisible();
- await expect(button("Message helper")).toBeVisible();
- await button("Dismiss notice").click();
- await button("Dismiss notice and continue").click();
- await expect(button("Save workflow")).toBeEnabled();
- expect(await page.evaluate(() => window.workflowFixture.calls.delete)).toBe(
- 1,
- );
-});
-
-test("invalid timeout text stays in the draft and blocks saves in both editor modes", async ({
- page,
-}) => {
- await page.goto(url);
- const button = (name) => page.getByRole("button", { name, exact: true });
- await button("Message helper").click();
- await page.getByText("Step options", { exact: true }).click();
- const timeout = page.getByLabel("Step timeout (optional)", { exact: true });
- const yaml = page.getByLabel("Workflow YAML", { exact: true });
- for (const input of ["oops", "0s", "1.5", "9007199254740992"]) {
- await timeout.fill(input);
- await expect(timeout).toHaveValue(input);
- await expect(button("Save workflow")).toBeDisabled();
- await expect(
- page.getByRole("status").filter({ hasText: /timeout/ }),
- ).toContainText("positive whole number");
- await page.getByRole("tab", { name: "YAML", exact: true }).click();
- expect(parseYaml(await yaml.inputValue()).steps[0].timeout_secs).toBe(
- input,
- );
- await expect(button("Save workflow")).toBeDisabled();
- await page.getByRole("tab", { name: "Form", exact: true }).click();
- await page.getByText("Step options", { exact: true }).click();
- await expect(timeout).toHaveValue(input);
- }
- await button("Close editor").click();
- await expect(
- page.getByRole("alertdialog", { name: "Leave this draft?" }),
- ).toBeVisible();
- await button("Keep editing").click();
- await expect(timeout).toHaveValue("9007199254740992");
- await timeout.fill("5m");
- await expect(button("Save workflow")).toBeEnabled();
- await button("Save workflow").click();
- await expect
- .poll(() => page.evaluate(() => window.workflowFixture.calls.save))
- .toBe(1);
- expect(
- parseYaml(await page.evaluate(() => window.workflowFixture.input().yaml))
- .steps[0].timeout_secs,
- ).toBe(300);
- await page.evaluate(() => window.workflowFixture.finish("succeeded"));
- await expect(button("Save workflow")).toBeEnabled();
- if (!(await timeout.isVisible()))
- await page.getByText("Step options", { exact: true }).click();
- await timeout.fill(" ");
- await button("Save workflow").click();
- await expect
- .poll(() => page.evaluate(() => window.workflowFixture.calls.save))
- .toBe(2);
- expect(
- parseYaml(await page.evaluate(() => window.workflowFixture.input().yaml))
- .steps[0],
- ).not.toHaveProperty("timeout_secs");
-});
-
-test("both Add actions allocate unused IDs after a very large parsed ID", async ({
- page,
-}) => {
- await page.goto(url);
- const button = (name) => page.getByRole("button", { name, exact: true });
- await button("Message helper").click();
- await page.getByRole("tab", { name: "YAML", exact: true }).click();
- const yaml = page.getByLabel("Workflow YAML", { exact: true });
- const definition = parseYaml(await yaml.inputValue());
- definition.steps[0].id = "step_9007199254740992";
- // JSON is YAML, and avoids testing a second serializer in this browser fixture.
- await yaml.fill(JSON.stringify(definition));
- await page.getByRole("tab", { name: "Form", exact: true }).click();
- await button("Add Send Message").click();
- await page
- .getByLabel("Message text", { exact: true })
- .nth(1)
- .fill("Another message");
- await button("Add Delay").click();
- await page.getByRole("tab", { name: "YAML", exact: true }).click();
- expect(
- parseYaml(await yaml.inputValue()).steps.map((step) => step.id),
- ).toEqual(["step_9007199254740992", "step_1", "step_2"]);
-});
-
test("real session reconnect retains unsaved YAML and an in-flight returned run ID", async ({
page,
}) => {