From 7860e2a1d885d95c27d3812928de92ffcc7f9ba2 Mon Sep 17 00:00:00 2001 From: Viktor Avelino <64113566+viktoravelino@users.noreply.github.com> Date: Mon, 27 Apr 2026 10:45:57 -0400 Subject: [PATCH] feat: refresh deployed success step (#12853) * feat(deploy): refresh deployed success step * refactor(deploy): split modal sections * test(deploy): cover success step * fix(deploy): rename flows step copy * fix(tests): update deployment test selectors for renamed step heading Tests were failing because the UI step heading changed from "Attach Flows" to "Flows". Updated three test files to use the new selector text. Co-Authored-By: Claude Haiku 4.5 * test(deployments): fix stepper e2e selectors --------- Co-authored-by: Claude Haiku 4.5 --- .../deployment-stepper-footer.test.tsx | 75 ++++++++++++ .../deployment-stepper-modal.test.tsx | 4 +- .../deployment-success-content.test.tsx | 86 +++++++++++++ .../__tests__/step-attach-flows.test.tsx | 8 +- .../components/deployment-stepper-footer.tsx | 89 ++++++++++++++ .../components/deployment-stepper-modal.tsx | 114 ++++++++---------- .../components/deployment-stepper.tsx | 32 +++-- .../components/deployment-success-content.tsx | 63 ++++++++++ .../step-attach-flows-flow-list-panel.tsx | 2 +- .../components/step-attach-flows.tsx | 2 +- .../core/features/deployment-create.spec.ts | 59 ++++++--- .../core/features/deployment-edit.spec.ts | 62 +++++++--- 12 files changed, 480 insertions(+), 116 deletions(-) create mode 100644 src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/deployment-stepper-footer.test.tsx create mode 100644 src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/deployment-success-content.test.tsx create mode 100644 src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-stepper-footer.tsx create mode 100644 src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-success-content.tsx diff --git a/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/deployment-stepper-footer.test.tsx b/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/deployment-stepper-footer.test.tsx new file mode 100644 index 0000000000..7e721a9c74 --- /dev/null +++ b/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/deployment-stepper-footer.test.tsx @@ -0,0 +1,75 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import DeploymentStepperFooter from "../components/deployment-stepper-footer"; + +jest.mock( + "@/components/common/genericIconComponent", + () => + function MockIcon({ name }: { name: string }) { + return ; + }, +); + +describe("DeploymentStepperFooter", () => { + it("shows done state controls when deployed", () => { + render( + , + ); + + expect(screen.getByRole("button", { name: "Done" })).toBeInTheDocument(); + expect( + screen.queryByRole("button", { name: "Cancel" }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole("button", { name: "Back" }), + ).not.toBeInTheDocument(); + expect( + screen.queryByTestId("deployment-stepper-next"), + ).not.toBeInTheDocument(); + }); + + it("calls onClose from done button", async () => { + const user = userEvent.setup(); + const onClose = jest.fn(); + + render( + , + ); + + await user.click(screen.getByRole("button", { name: "Done" })); + + expect(onClose).toHaveBeenCalled(); + }); +}); diff --git a/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/deployment-stepper-modal.test.tsx b/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/deployment-stepper-modal.test.tsx index 4640affa27..a719aa06d8 100644 --- a/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/deployment-stepper-modal.test.tsx +++ b/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/deployment-stepper-modal.test.tsx @@ -345,7 +345,7 @@ describe("Step labels", () => { renderModal(); expect(screen.getByText("Provider")).toBeInTheDocument(); expect(screen.getByText("Type")).toBeInTheDocument(); - expect(screen.getByText("Attach Flows")).toBeInTheDocument(); + expect(screen.getByText("Flows")).toBeInTheDocument(); expect(screen.getByText("Review")).toBeInTheDocument(); }); @@ -355,7 +355,7 @@ describe("Step labels", () => { renderModal({ editingDeployment: makeDeployment() }); expect(screen.queryByText("Provider")).not.toBeInTheDocument(); expect(screen.getByText("Type")).toBeInTheDocument(); - expect(screen.getByText("Attach Flows")).toBeInTheDocument(); + expect(screen.getByText("Flows")).toBeInTheDocument(); expect(screen.getByText("Review")).toBeInTheDocument(); }); }); diff --git a/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/deployment-success-content.test.tsx b/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/deployment-success-content.test.tsx new file mode 100644 index 0000000000..2fab3d6881 --- /dev/null +++ b/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/deployment-success-content.test.tsx @@ -0,0 +1,86 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import DeploymentSuccessContent from "../components/deployment-success-content"; + +jest.mock( + "@/components/common/genericIconComponent", + () => + function MockIcon({ name }: { name: string }) { + return ; + }, +); + +describe("DeploymentSuccessContent", () => { + it("renders success copy and provider link", () => { + render( + , + ); + + expect(screen.getByText("Deployment successful")).toBeInTheDocument(); + expect( + screen.getByText("Deployed to watsonx Orchestrate as draft"), + ).toBeInTheDocument(); + expect( + screen.getByRole("link", { name: /watsonx Orchestrate/i }), + ).toHaveAttribute( + "href", + "https://www.ibm.com/products/watsonx-orchestrate", + ); + }); + + it("shows test button and calls handler", async () => { + const user = userEvent.setup(); + const onTest = jest.fn(); + + render( + , + ); + + await user.click(screen.getByRole("button", { name: "Test Deployment" })); + + expect(onTest).toHaveBeenCalled(); + }); + + it("hides test button without deployment name", () => { + render( + , + ); + + expect( + screen.queryByRole("button", { name: "Test Deployment" }), + ).not.toBeInTheDocument(); + }); + + it("hides test button when showTestButton is false", () => { + render( + , + ); + + expect( + screen.queryByRole("button", { name: "Test Deployment" }), + ).not.toBeInTheDocument(); + }); +}); diff --git a/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/step-attach-flows.test.tsx b/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/step-attach-flows.test.tsx index 49284ece5f..aa0375dd3a 100644 --- a/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/step-attach-flows.test.tsx +++ b/src/frontend/src/pages/MainPage/pages/deploymentsPage/__tests__/step-attach-flows.test.tsx @@ -230,14 +230,14 @@ beforeEach(() => { // --------------------------------------------------------------------------- describe("Three-panel layout rendering", () => { - it("renders the Attach Flows heading", () => { + it("renders the Flows heading", () => { render(); - expect(screen.getByText("Attach Flows")).toBeInTheDocument(); + expect(screen.getByText("Flows")).toBeInTheDocument(); }); - it("renders the Available Flows panel header", () => { + it("renders the Available panel header", () => { render(); - expect(screen.getByText("Available Flows")).toBeInTheDocument(); + expect(screen.getByText("Available")).toBeInTheDocument(); }); it("renders flow items in the list", () => { diff --git a/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-stepper-footer.tsx b/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-stepper-footer.tsx new file mode 100644 index 0000000000..ef0a056d99 --- /dev/null +++ b/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-stepper-footer.tsx @@ -0,0 +1,89 @@ +import ForwardedIconComponent from "@/components/common/genericIconComponent"; +import { Button } from "@/components/ui/button"; + +interface DeploymentStepperFooterProps { + canGoNext: boolean; + currentStep: number; + isCreatingAccount: boolean; + isDeployed: boolean; + isDeploying: boolean; + isInDeployPhase: boolean; + isFinalStep: boolean; + minStep: number; + actionIcon: string; + actionLabel: string; + progressLabel: string; + onBack: () => void; + onCancel: () => void; + onClose: () => void; + onPrimaryAction: () => void; +} + +export default function DeploymentStepperFooter({ + canGoNext, + currentStep, + isCreatingAccount, + isDeployed, + isDeploying, + isInDeployPhase, + isFinalStep, + minStep, + actionIcon, + actionLabel, + progressLabel, + onBack, + onCancel, + onClose, + onPrimaryAction, +}: DeploymentStepperFooterProps) { + return ( +
+ {isDeployed ? ( +
+ ) : ( + + )} +
+ {!isDeployed && ( + + )} + {!isInDeployPhase && ( + + )} + {isDeploying && ( + + )} + {isDeployed && } +
+
+ ); +} diff --git a/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-stepper-modal.tsx b/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-stepper-modal.tsx index fc4d5c99ee..7bfabc79a3 100644 --- a/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-stepper-modal.tsx +++ b/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-stepper-modal.tsx @@ -1,7 +1,5 @@ import { useMemo, useState } from "react"; import { useParams } from "react-router-dom"; -import ForwardedIconComponent from "@/components/common/genericIconComponent"; -import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, @@ -21,7 +19,9 @@ import { } from "../contexts/deployment-stepper-context"; import { useErrorAlert } from "../hooks/use-error-alert"; import type { Deployment, DeploymentProvider, ProviderAccount } from "../types"; -import DeploymentStepper from "./deployment-stepper"; +import DeploymentStepper, { CREATE_DEPLOYED_STEPS } from "./deployment-stepper"; +import DeploymentStepperFooter from "./deployment-stepper-footer"; +import DeploymentSuccessContent from "./deployment-success-content"; import StepAttachFlows from "./step-attach-flows"; import StepDeployStatus from "./step-deploy-status"; import StepProvider from "./step-provider"; @@ -206,6 +206,7 @@ function DeploymentStepperModalContent({ canGoNext, handleNext, handleBack, + selectedProvider, selectedInstance, setSelectedInstance, needsProviderAccountCreation, @@ -225,6 +226,8 @@ function DeploymentStepperModalContent({ const isDeploying = deploymentPhase === "deploying"; const isDeployed = deploymentPhase === "deployed"; const isInDeployPhase = isDeploying || isDeployed; + const providerConsoleUrl = "https://www.ibm.com/products/watsonx-orchestrate"; + const providerDisplayName = selectedProvider?.name ?? "watsonx Orchestrate"; // In edit mode, steps are shifted: 1=Type, 2=Attach, 3=Review. const logicalStep = isEditMode ? currentStep + 1 : currentStep; @@ -316,19 +319,40 @@ function DeploymentStepperModalContent({ className="text-center text-2xl font-semibold" data-testid="stepper-modal-title" > - {isEditMode ? "Update Deployment" : "Create New Deployment"} + {isDeployed && !isEditMode + ? "Deployed" + : isEditMode + ? "Update Deployment" + : "Create New Deployment"} - +
{/* Content box: step content + footer */}
{isInDeployPhase ? ( - + isDeployed && !isEditMode ? ( + + ) : ( + + ) ) : ( <> {logicalStep === 1 && } @@ -339,61 +363,23 @@ function DeploymentStepperModalContent({ )}
- {/* Footer */} -
- -
- {!isDeployed && ( - - )} - {!isInDeployPhase && ( - - )} - {isDeploying && ( - - )} - {isDeployed && onTestDeployment && !isEditMode && ( - - )} -
-
+ setOpen(false)} + onClose={() => setOpen(false)} + onPrimaryAction={isFinalStep ? handleDeploy : handleStepNext} + />
); diff --git a/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-stepper.tsx b/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-stepper.tsx index 903abf6144..3abc44b849 100644 --- a/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-stepper.tsx +++ b/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-stepper.tsx @@ -1,25 +1,41 @@ import { cn } from "@/utils/utils"; import { useDeploymentStepper } from "../contexts/deployment-stepper-context"; -const CREATE_STEPS = [ +export const CREATE_STEPS = [ { number: 1, label: "Provider" }, { number: 2, label: "Type" }, - { number: 3, label: "Attach Flows" }, + { number: 3, label: "Flows" }, { number: 4, label: "Review" }, ] as const; +export const CREATE_DEPLOYED_STEPS = [ + { number: 1, label: "Provider" }, + { number: 2, label: "Type" }, + { number: 3, label: "Flows" }, + { number: 4, label: "Deployed" }, +] as const; + const EDIT_STEPS = [ { number: 1, label: "Type" }, - { number: 2, label: "Attach Flows" }, + { number: 2, label: "Flows" }, { number: 3, label: "Review" }, ] as const; export const DEPLOYMENT_STEPS = CREATE_STEPS; -export default function DeploymentStepper() { +interface DeploymentStepperProps { + steps?: readonly { number: number; label: string }[]; + currentStepOverride?: number; +} + +export default function DeploymentStepper({ + steps: stepsProp, + currentStepOverride, +}: DeploymentStepperProps) { const { currentStep, isEditMode } = useDeploymentStepper(); - const steps = isEditMode ? EDIT_STEPS : CREATE_STEPS; - const progressPercent = ((currentStep - 1) / (steps.length - 1)) * 100; + const steps = stepsProp ?? (isEditMode ? EDIT_STEPS : CREATE_STEPS); + const activeStep = currentStepOverride ?? currentStep; + const progressPercent = ((activeStep - 1) / (steps.length - 1)) * 100; return (
@@ -35,7 +51,7 @@ export default function DeploymentStepper() {
= step.number + activeStep >= step.number ? "bg-foreground text-background" : "bg-muted text-muted-foreground", )} @@ -45,7 +61,7 @@ export default function DeploymentStepper() { = step.number && "font-medium", + activeStep >= step.number && "font-medium", )} > {step.label} diff --git a/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-success-content.tsx b/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-success-content.tsx new file mode 100644 index 0000000000..1cf6280749 --- /dev/null +++ b/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/deployment-success-content.tsx @@ -0,0 +1,63 @@ +import ForwardedIconComponent from "@/components/common/genericIconComponent"; +import { Button } from "@/components/ui/button"; + +interface DeploymentSuccessContentProps { + deploymentName?: string; + providerName: string; + providerUrl: string; + showTestButton: boolean; + onTest: () => void; +} + +export default function DeploymentSuccessContent({ + deploymentName, + providerName, + providerUrl, + showTestButton, + onTest, +}: DeploymentSuccessContentProps) { + return ( +
+
+ +
+ +
+

+ Deployment successful +

+

+ Deployed to {providerName} as draft +

+
+ +
+ Publish from Draft to Live in + + {providerName} + + +
+ + {showTestButton && deploymentName && ( + + )} +
+ ); +} diff --git a/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/step-attach-flows-flow-list-panel.tsx b/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/step-attach-flows-flow-list-panel.tsx index 6303ed25d3..6a5ba993bd 100644 --- a/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/step-attach-flows-flow-list-panel.tsx +++ b/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/step-attach-flows-flow-list-panel.tsx @@ -29,7 +29,7 @@ export const FlowListPanel = memo(function FlowListPanel({ return (
- Available Flows + Available
{flows.map((flow) => { diff --git a/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/step-attach-flows.tsx b/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/step-attach-flows.tsx index 1beac14672..07141246e8 100644 --- a/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/step-attach-flows.tsx +++ b/src/frontend/src/pages/MainPage/pages/deploymentsPage/components/step-attach-flows.tsx @@ -259,7 +259,7 @@ export default function StepAttachFlows() { return (
-

Attach Flows

+

Flows

{ test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -220,7 +225,9 @@ test( // --------------------------------------------------------------------------- test( "deployment-create: step 1 provider - Next disabled without selection, enabled after selecting", - { tag: ["@deployment", "@workspace"] }, + { + tag: ["@deployment", "@workspace"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -245,7 +252,9 @@ test( // --------------------------------------------------------------------------- test( "deployment-create: step 2 type - fill name and select type to enable Next", - { tag: ["@deployment", "@workspace"] }, + { + tag: ["@deployment", "@workspace"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -280,11 +289,13 @@ test( ); // --------------------------------------------------------------------------- -// Test 4: Step 3 (Attach Flows) — select a flow and version, Next enables +// Test 4: Step 3 (Flows) — select a flow and version, Next enables // --------------------------------------------------------------------------- test( "deployment-create: step 3 attach flows - select flow and version enables Next", - { tag: ["@deployment", "@workspace"] }, + { + tag: ["@deployment", "@workspace"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -331,7 +342,9 @@ test( // --------------------------------------------------------------------------- test( "deployment-create: step 4 review - shows review content and Deploy button text", - { tag: ["@deployment", "@workspace"] }, + { + tag: ["@deployment", "@workspace"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -361,7 +374,9 @@ test( // --------------------------------------------------------------------------- test( "deployment-create: clicking Deploy triggers POST and shows deploy status", - { tag: ["@deployment", "@workspace"] }, + { + tag: ["@deployment", "@workspace"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -414,7 +429,9 @@ test( // --------------------------------------------------------------------------- test( "deployment-create: user can change tool name on review step", - { tag: ["@deployment", "@workspace"] }, + { + tag: ["@deployment", "@workspace"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -451,7 +468,9 @@ test( // --------------------------------------------------------------------------- test( "deployment-create: review step shows error when tool name already exists in provider", - { tag: ["@deployment", "@workspace"] }, + { + tag: ["@deployment", "@workspace"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -476,7 +495,9 @@ test( // --------------------------------------------------------------------------- test( "deployment-create: review step shows no error when tool name is unique", - { tag: ["@deployment", "@workspace"] }, + { + tag: ["@deployment", "@workspace"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -501,7 +522,9 @@ test( // --------------------------------------------------------------------------- test( "deployment-create: editing tool name to unique value clears duplicate error", - { tag: ["@deployment", "@workspace"] }, + { + tag: ["@deployment", "@workspace"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", diff --git a/src/frontend/tests/core/features/deployment-edit.spec.ts b/src/frontend/tests/core/features/deployment-edit.spec.ts index fbec55b3f2..ba8062ccae 100644 --- a/src/frontend/tests/core/features/deployment-edit.spec.ts +++ b/src/frontend/tests/core/features/deployment-edit.spec.ts @@ -83,9 +83,23 @@ async function openEditDialog(page: Parameters[2]["page"]) { await page.waitForSelector('[data-testid="stepper-modal-title"]'); } +async function expectDeploymentTypeStep( + page: Parameters[2]["page"], +) { + await expect( + page.getByRole("heading", { name: /Deployment Type/i }), + ).toBeVisible(); +} + +async function expectFlowsStep(page: Parameters[2]["page"]) { + await expect(page.getByRole("heading", { name: /^Flows$/i })).toBeVisible(); +} + test( "Opens edit stepper from actions menu", - { tag: ["@release", "@workspace", "@api"] }, + { + tag: ["@release", "@workspace", "@api"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -110,7 +124,9 @@ test( test( "Edit mode skips provider step — starts at Type", - { tag: ["@release", "@workspace", "@api"] }, + { + tag: ["@release", "@workspace", "@api"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -126,7 +142,7 @@ test( await openEditDialog(page); // Wait for stepper body to render (parallel fetches must complete) - await page.waitForSelector('h2:has-text("Deployment Type")'); + await expectDeploymentTypeStep(page); await expect(page.getByText("Deployment Type")).toBeVisible(); @@ -141,7 +157,9 @@ test( test( "Name field pre-populated in edit mode", - { tag: ["@release", "@workspace", "@api"] }, + { + tag: ["@release", "@workspace", "@api"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -157,7 +175,7 @@ test( await openEditDialog(page); // Wait for stepper body to render - await page.waitForSelector('h2:has-text("Deployment Type")'); + await expectDeploymentTypeStep(page); const nameInput = page.getByPlaceholder("e.g., Sales Bot"); await expect(nameInput).toHaveValue("Test Deployment"); @@ -166,7 +184,9 @@ test( test( "Submitting PATCH closes modal", - { tag: ["@release", "@workspace", "@api"] }, + { + tag: ["@release", "@workspace", "@api"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -182,13 +202,13 @@ test( await openEditDialog(page); // Wait for stepper body to render (parallel fetches must complete) - await page.waitForSelector('h2:has-text("Deployment Type")'); + await expectDeploymentTypeStep(page); // Navigate through the stepper steps to reach Review // Step: Type → click Next await page.getByTestId("deployment-stepper-next").click(); - // Step: Attach Flows → click Next + // Step: Flows → click Next await page.getByTestId("deployment-stepper-next").click(); // Step: Review → click Update (final step) @@ -209,7 +229,9 @@ test( test( "Cancel during edit closes modal without calling PATCH", - { tag: ["@release", "@workspace", "@api"] }, + { + tag: ["@release", "@workspace", "@api"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -225,7 +247,7 @@ test( await openEditDialog(page); // Wait for stepper body to render - await page.waitForSelector('h2:has-text("Deployment Type")'); + await expectDeploymentTypeStep(page); let patchCalled = false; page.on("request", (req) => { @@ -356,7 +378,9 @@ async function setupRoutesWithConnections( // --------------------------------------------------------------------------- test( "Edit mode includes new connections in PATCH request", - { tag: ["@release", "@workspace", "@api"] }, + { + tag: ["@release", "@workspace", "@api"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -399,11 +423,11 @@ test( await page.waitForSelector('[data-testid="stepper-modal-title"]'); // Step 1 (Type) → Next - await page.waitForSelector('h2:has-text("Deployment Type")'); + await expectDeploymentTypeStep(page); await page.getByTestId("deployment-stepper-next").click(); - // Step 2 (Attach Flows) — flow "f1" should already be attached - await page.waitForSelector("text=Attach Flows"); + // Step 2 (Flows) — flow "f1" should already be attached + await expectFlowsStep(page); await page.waitForSelector('[data-testid="flow-item-f1"]'); // Click the pre-attached flow and its version to open the connection panel @@ -455,7 +479,9 @@ test( // --------------------------------------------------------------------------- test( "Edit mode includes removed connections in PATCH request", - { tag: ["@release", "@workspace", "@api"] }, + { + tag: ["@release", "@workspace", "@api"], + }, async ({ page }) => { test.skip( process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true", @@ -497,11 +523,11 @@ test( await page.waitForSelector('[data-testid="stepper-modal-title"]'); // Step 1 (Type) → Next - await page.waitForSelector('h2:has-text("Deployment Type")'); + await expectDeploymentTypeStep(page); await page.getByTestId("deployment-stepper-next").click(); - // Step 2 (Attach Flows) - await page.waitForSelector("text=Attach Flows"); + // Step 2 (Flows) + await expectFlowsStep(page); await page.waitForSelector('[data-testid="flow-item-f1"]'); await page.getByTestId("flow-item-f1").click(); await page.waitForSelector('[data-testid="version-item-fv1"]');