From 8b4f9d6d3fa017690f77fd4fe2f67f54d443419a Mon Sep 17 00:00:00 2001 From: Logan Johnson Date: Tue, 8 Sep 2026 02:22:43 -0400 Subject: [PATCH] test(desktop): disambiguate workflow step text fills and settle inspector geometry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Message text" label exists in both the outgoing trigger pane and the newly added send-message step pane; during the inspector's 150ms AnimatePresence mode="wait" exit the unscoped dialog.getByLabel resolves to the single mounted trigger input, so the step fill overwrote the trigger condition and left the step text empty. On the slow CI runner this corrupted the saved definition: workflows.spec.ts then waited forever for the "Turn on" activation dialog that a filtered trigger correctly suppresses, and workflow-local-controls.spec.ts reopened on the YAML tab with no Trigger node button. Scope the five step-intended fills to the step textarea id (wf-step--text) so the fill waits for the intended pane to mount; trigger-intended "Message text" bindings and all assertions stay unchanged. The reopened-inspector geometry sampled operator boundingBoxes up to ~160ms into the 240ms inspector column entry animation (waitForAnimations ran only after the comparisons). Settle with the shared waitForAnimations boundary before sampling; geometry thresholds are untouched. Trace evidence: CI run 34188379868, job 101941389576 — the step fill resolved to wf-trigger-trigger_text-value during the exit window. Signed-off-by: Logan Johnson --- .../tests/e2e/workflow-local-controls.spec.ts | 16 +++++++++++++-- desktop/tests/e2e/workflows.spec.ts | 20 ++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/desktop/tests/e2e/workflow-local-controls.spec.ts b/desktop/tests/e2e/workflow-local-controls.spec.ts index 6a8319082c..d897168c88 100644 --- a/desktop/tests/e2e/workflow-local-controls.spec.ts +++ b/desktop/tests/e2e/workflow-local-controls.spec.ts @@ -63,7 +63,13 @@ async function addMessageStep( ) { await dialog.getByRole("button", { name: "Add step", exact: true }).click(); await page.getByRole("menuitem", { name: "Send Message" }).click(); - await dialog.getByLabel("Message text").fill("Workflow notification"); + // "Message text" is also the outgoing trigger pane's condition label while + // the inspector exit transition (AnimatePresence mode="wait", 150ms) still + // mounts it. Scope to the step textarea id so the fill waits for the + // intended step pane instead of racing the outgoing trigger input. + await dialog + .locator('textarea[id^="wf-step-"][id$="-text"]') + .fill("Workflow notification"); } async function createEnabled( @@ -152,7 +158,9 @@ test("inserts template variables with keyboard control and restores the caret", await dialog.getByRole("button", { name: "Add step", exact: true }).click(); await page.getByRole("menuitem", { name: "Send Message" }).click(); - const textarea = dialog.getByLabel("Message text"); + // Step-scoped (see addMessageStep): the label is ambiguous during the + // inspector exit transition. + const textarea = dialog.locator('textarea[id^="wf-step-"][id$="-text"]'); const listbox = page.getByRole("listbox"); await textarea.fill("Hello {{trig"); await expect(listbox).toBeVisible(); @@ -286,6 +294,10 @@ test("round-trips and reopens structured message-text conditions", async ({ await dialog.getByRole("tab", { name: "Form" }).click(); await openTriggerInspector(dialog); + // The inspector column enters through a 240ms opacity/width/x animation; + // settle it before sampling the operator geometry so each boundingBox is + // read from the final layout, not mid-animation positions. + await waitForAnimations(page); const matchControls = dialog.getByRole("group", { name: "Match" }); const operatorButtons = matchControls.getByRole("button"); const firstOperatorBox = await operatorButtons.nth(0).boundingBox(); diff --git a/desktop/tests/e2e/workflows.spec.ts b/desktop/tests/e2e/workflows.spec.ts index 16844a2f3e..dd9fbf41e1 100644 --- a/desktop/tests/e2e/workflows.spec.ts +++ b/desktop/tests/e2e/workflows.spec.ts @@ -96,7 +96,13 @@ async function createWorkflow( await dialog.getByRole("button", { name: "Add step", exact: true }).click(); await page.getByRole("menuitem", { name: "Send Message" }).click(); - await dialog.getByLabel("Message text").fill("Workflow notification"); + // "Message text" is also the outgoing trigger pane's condition label while the + // inspector exit transition (AnimatePresence mode="wait", 150ms) still mounts + // it. Scope to the step textarea id so the fill waits for the intended step + // pane instead of racing the outgoing trigger input. + await dialog + .locator('textarea[id^="wf-step-"][id$="-text"]') + .fill("Workflow notification"); if (options?.stepName) { await dialog.getByRole("button", { name: "Step details" }).click(); await dialog.getByLabel("Name (optional)").fill(options.stepName); @@ -700,7 +706,11 @@ test("captures the built editor at desktop and narrow widths", async ({ await editWorkflowName(dialog, "editor_screenshot"); await dialog.getByRole("button", { name: "Add step", exact: true }).click(); await page.getByRole("menuitem", { name: "Send Message" }).click(); - await dialog.getByLabel("Message text").fill("Notify the workflow channel"); + // Step-scoped (see createWorkflow): the label is ambiguous during the + // inspector exit transition. + await dialog + .locator('textarea[id^="wf-step-"][id$="-text"]') + .fill("Notify the workflow channel"); const inspector = dialog.getByTestId("workflow-node-inspector"); for (const viewport of [ @@ -805,7 +815,11 @@ test("pane routes use stable IDs and Form/YAML changes stay synchronized", async await dialog.getByRole("button", { name: "Add step", exact: true }).click(); await page.getByRole("menuitem", { name: "Send Message" }).click(); - await dialog.getByLabel("Message text").fill("first message"); + // Step-scoped (see createWorkflow): the label is ambiguous during the + // inspector exit transition. + await dialog + .locator('textarea[id^="wf-step-"][id$="-text"]') + .fill("first message"); await expect(page).toHaveURL(/pane=step%3Astep_1/); await dialog.getByRole("button", { name: "Add after Step 1" }).click();