test: Refactor Playwright suite with shared helpers and remove redundant tests (#13278)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Cristhian Zanforlin Lousa
2026-05-22 14:35:04 -03:00
committed by GitHub
parent cf3c2d30c4
commit 63ee455a2b
199 changed files with 2551 additions and 4025 deletions

View File

@ -1,7 +1,8 @@
import { test } from "../../fixtures";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
test(
"select and delete a flow",
{ tag: ["@release", "@mainpage"] },
@ -9,7 +10,9 @@ test(
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 100000,
@ -26,15 +29,17 @@ test(
timeout: 1000,
});
// click on the delete button
await page.getByText("Delete").last().click();
await page.getByText(TEXTS.delete).last().click();
await page.getByText("This can't be undone.").isVisible({
timeout: 1000,
});
//confirm the deletion in the modal
await page.getByText("Delete").last().click();
await page.getByText(TEXTS.delete).last().click();
await page.getByText("Selected items deleted successfully").isVisible();
await expect(
page.getByText("Selected items deleted successfully"),
).toBeVisible();
},
);
@ -42,7 +47,9 @@ test("search flows", { tag: ["@release", "@mainpage"] }, async ({ page }) => {
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 100000,
@ -50,7 +57,7 @@ test("search flows", { tag: ["@release", "@mainpage"] }, async ({ page }) => {
await page.getByTestId("icon-ChevronLeft").first().click();
await page.getByText("New Flow").isVisible();
await expect(page.getByText("New Flow")).toBeVisible();
await page.getByTestId("new-project-btn").click();
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Memory Chatbot" }).click();
@ -70,9 +77,11 @@ test("search flows", { tag: ["@release", "@mainpage"] }, async ({ page }) => {
await page.getByTestId("icon-ChevronLeft").first().click();
await page.getByPlaceholder("Search flows").fill("Memory Chatbot");
await page.getByText("Memory Chatbot", { exact: true }).isVisible();
await expect(page.getByText("Memory Chatbot", { exact: true })).toBeVisible();
await page.getByText("Document Q&A", { exact: true }).isHidden();
await page.getByText("Basic Prompting", { exact: true }).isHidden();
await page
.getByText(TEXTS.templateBasicPrompting, { exact: true })
.isHidden();
});
test(
@ -83,11 +92,13 @@ test(
if (await page.getByTestId("components-btn").isVisible()) {
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await adjustScreenView(page, { numberOfZoomOut: 2 });
await page.getByText("Chat Input").first().click();
await page.getByText(TEXTS.componentChatInput).first().click();
await page.waitForSelector('[data-testid="more-options-modal"]', {
timeout: 1000,
});
@ -123,15 +134,19 @@ test(
await page.getByTestId("icon-ChevronLeft").first().click();
const exitButton = await page.getByText("Exit", { exact: true }).count();
const exitButton = await page
.getByText(TEXTS.exit, { exact: true })
.count();
if (exitButton > 0) {
await page.getByText("Exit", { exact: true }).click();
await page.getByText(TEXTS.exit, { exact: true }).click();
}
await page.getByTestId("components-btn").click();
await page.getByPlaceholder("Search components").fill("Chat Input");
await page.getByText("Chat Input", { exact: true }).isVisible();
await expect(
page.getByText(TEXTS.componentChatInput, { exact: true }),
).toBeVisible();
await page.getByText("Prompt", { exact: true }).isHidden();
await page.getByText("OpenAI", { exact: true }).isHidden();
}

View File

@ -5,6 +5,7 @@ import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { renameFlow } from "../../utils/rename-flow";
import { zoomOut } from "../../utils/zoom-out";
import { TEXTS } from "../../utils/constants/texts";
test(
"when auto_login is false, admin can CRUD user's and should see just your own flows",
{ tag: ["@release", "@api", "@database", "@mainpage"] },
@ -41,16 +42,22 @@ test(
await page.goto("/");
await page.waitForSelector("text=sign in to langflow", { timeout: 30000 });
await page.waitForSelector(`text=${TEXTS.authSignInHeader}`, {
timeout: 30000,
});
await page.getByPlaceholder("Username").fill("langflow");
await page.getByPlaceholder("Password").fill("langflow");
await page
.getByPlaceholder(TEXTS.placeholderUsername)
.fill(TEXTS.authDefaultCredential);
await page
.getByPlaceholder(TEXTS.placeholderPassword)
.fill(TEXTS.authDefaultCredential);
await page.evaluate(() => {
sessionStorage.removeItem("testMockAutoLogin");
});
await page.getByRole("button", { name: "Sign In" }).click();
await page.getByRole("button", { name: TEXTS.signIn }).click();
await page.waitForSelector('[data-testid="mainpage_title"]', {
timeout: 30000,
@ -67,7 +74,10 @@ test(
//CRUD an user
await page.getByText("New User", { exact: true }).click();
await page.getByPlaceholder("Username").last().fill(randomName);
await page
.getByPlaceholder(TEXTS.placeholderUsername)
.last()
.fill(randomName);
await page.locator('input[name="password"]').fill(randomPassword);
await page.locator('input[name="confirmpassword"]').fill(randomPassword);
@ -77,7 +87,7 @@ test(
await page.locator("#is_active").click();
await page.getByText("Save", { exact: true }).click();
await page.getByText(TEXTS.save, { exact: true }).click();
await page.waitForSelector("text=new user added", { timeout: 30000 });
@ -86,7 +96,7 @@ test(
});
await page.getByTestId("icon-Trash2").last().click();
await page.getByText("Delete", { exact: true }).last().click();
await page.getByText(TEXTS.delete, { exact: true }).last().click();
await page.waitForSelector("text=user deleted", { timeout: 30000 });
@ -97,7 +107,10 @@ test(
await page.getByText("New User", { exact: true }).click();
await page.getByPlaceholder("Username").last().fill(randomName);
await page
.getByPlaceholder(TEXTS.placeholderUsername)
.last()
.fill(randomName);
await page.locator('input[name="password"]').fill(randomPassword);
await page.locator('input[name="confirmpassword"]').fill(randomPassword);
@ -107,7 +120,7 @@ test(
await page.locator("#is_active").click();
await page.getByText("Save", { exact: true }).click();
await page.getByText(TEXTS.save, { exact: true }).click();
await page.waitForSelector("text=new user added", { timeout: 30000 });
@ -115,14 +128,20 @@ test(
(response) =>
response.url().includes("/api/v1/users") && response.status() === 200,
);
await page.getByPlaceholder("Username").last().fill(randomName);
await page
.getByPlaceholder(TEXTS.placeholderUsername)
.last()
.fill(randomName);
await searchResponse;
await page.getByTestId("icon-Pencil").last().click();
await page.getByPlaceholder("Username").last().fill(secondRandomName);
await page
.getByPlaceholder(TEXTS.placeholderUsername)
.last()
.fill(secondRandomName);
await page.getByText("Save", { exact: true }).click();
await page.getByText(TEXTS.save, { exact: true }).click();
await page.waitForSelector("text=user edited", { timeout: 30000 });
@ -146,7 +165,9 @@ test(
await awaitBootstrapTest(page, { skipGoto: true });
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await adjustScreenView(page, { numberOfZoomOut: 1 });
@ -182,18 +203,22 @@ test(
sessionStorage.setItem("testMockAutoLogin", "true");
});
await page.getByText("Logout", { exact: true }).click();
await page.getByText(TEXTS.logout, { exact: true }).click();
await page.waitForSelector("text=sign in to langflow", { timeout: 30000 });
await page.waitForSelector(`text=${TEXTS.authSignInHeader}`, {
timeout: 30000,
});
await page.getByPlaceholder("Username").fill(secondRandomName);
await page.getByPlaceholder("Password").fill(randomPassword);
await page
.getByPlaceholder(TEXTS.placeholderUsername)
.fill(secondRandomName);
await page.getByPlaceholder(TEXTS.placeholderPassword).fill(randomPassword);
await page.waitForSelector("text=Sign in", {
timeout: 1500,
});
await page.getByRole("button", { name: "Sign In" }).click();
await page.getByRole("button", { name: TEXTS.signIn }).click();
await page.evaluate(() => {
sessionStorage.removeItem("testMockAutoLogin");
@ -216,7 +241,9 @@ test(
await awaitBootstrapTest(page, { skipGoto: true });
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await adjustScreenView(page, { numberOfZoomOut: 2 });
@ -249,18 +276,24 @@ test(
sessionStorage.setItem("testMockAutoLogin", "true");
});
await page.getByText("Logout", { exact: true }).click();
await page.getByText(TEXTS.logout, { exact: true }).click();
await page.waitForSelector("text=sign in to langflow", { timeout: 30000 });
await page.waitForSelector(`text=${TEXTS.authSignInHeader}`, {
timeout: 30000,
});
await page.getByPlaceholder("Username").fill("langflow");
await page.getByPlaceholder("Password").fill("langflow");
await page
.getByPlaceholder(TEXTS.placeholderUsername)
.fill(TEXTS.authDefaultCredential);
await page
.getByPlaceholder(TEXTS.placeholderPassword)
.fill(TEXTS.authDefaultCredential);
await page.evaluate(() => {
sessionStorage.removeItem("testMockAutoLogin");
});
await page.getByRole("button", { name: "Sign In" }).click();
await page.getByRole("button", { name: TEXTS.signIn }).click();
await page.waitForSelector('[data-testid="mainpage_title"]', {
timeout: 30000,

View File

@ -3,6 +3,7 @@ import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import type { Page } from "@playwright/test";
import { TEXTS } from "../../utils/constants/texts";
test.describe("Bulk Delete Sessions", () => {
// Helper to send a message in the playground
async function sendMessage(page: Page, message: string) {
@ -73,12 +74,14 @@ test.describe("Bulk Delete Sessions", () => {
// Load a starter project
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await initialGPTsetup(page);
// Open playground
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForTimeout(2000);
@ -107,12 +110,14 @@ test.describe("Bulk Delete Sessions", () => {
// Load a starter project
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await initialGPTsetup(page);
// Open playground
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForTimeout(2000);
@ -156,12 +161,14 @@ test.describe("Bulk Delete Sessions", () => {
// Load a starter project
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await initialGPTsetup(page);
// Open playground
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForTimeout(2000);
@ -206,12 +213,14 @@ test.describe("Bulk Delete Sessions", () => {
// Load a starter project
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await initialGPTsetup(page);
// Open playground
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForTimeout(2000);
@ -266,12 +275,14 @@ test.describe("Bulk Delete Sessions", () => {
// Load a starter project
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await initialGPTsetup(page);
// Open playground
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForTimeout(2000);
@ -322,12 +333,14 @@ test.describe("Bulk Delete Sessions", () => {
// Load a starter project
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await initialGPTsetup(page);
// Open playground
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForTimeout(2000);
@ -383,12 +396,14 @@ test.describe("Bulk Delete Sessions", () => {
// Load a starter project
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await initialGPTsetup(page);
// Open playground
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForTimeout(2000);
@ -420,12 +435,14 @@ test.describe("Bulk Delete Sessions", () => {
// Load a starter project
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await initialGPTsetup(page);
// Open playground
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForTimeout(2000);

View File

@ -8,6 +8,7 @@ import {
openAdvancedOptions,
} from "../../utils/open-advanced-options";
import { TEXTS } from "../../utils/constants/texts";
test(
"user must be able to send an image on chat",
{ tag: ["@release", "@workspace", "@components"] },
@ -24,7 +25,9 @@ test(
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
});
@ -33,10 +36,12 @@ test(
await page.waitForSelector("text=Chat Input", { timeout: 30000 });
await page.getByText("Chat Input", { exact: true }).click();
await page.getByText(TEXTS.componentChatInput, { exact: true }).click();
await openAdvancedOptions(page);
await closeAdvancedOptions(page);
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForSelector('[data-testid="input-chat-playground"]', {
timeout: 100000,

View File

@ -1,6 +1,7 @@
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
test(
"user can add components by hovering and clicking the plus icon",
{ tag: ["@release", "@components", "@workspace"] },
@ -17,7 +18,7 @@ test(
// Search for a component
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat input");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchChatInput);
await page.waitForSelector('[data-testid="input_outputChat Input"]', {
timeout: 2000,

View File

@ -3,6 +3,7 @@ import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { removeOldApiKeys } from "../../utils/remove-old-api-keys";
import { TEXTS } from "../../utils/constants/texts";
test(
"user should be able to interact with composio component",
{ tag: ["@release", "@workspace", "@api", "@components"] },
@ -59,7 +60,7 @@ test(
await page.getByTestId("button_run_gmail").click();
await page.waitForSelector("text=built successfully", {
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 30000,
});

View File

@ -1,6 +1,7 @@
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
test(
"custom component code button should be pink when adding custom component",
{ tag: ["@release", "@components"] },
@ -63,7 +64,7 @@ class CustomComponent(Component):
await page.keyboard.press(`ControlOrMeta+A`);
await page.locator("textarea").last().fill(waitTimeoutCode);
await page.getByText("Check & Save").last().click();
await page.getByText(TEXTS.checkAndSave).last().click();
await expect(page.getByTestId("code-button-modal").last()).not.toHaveClass(
/animate-pulse-pink/,

View File

@ -18,6 +18,7 @@ import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { zoomOut } from "../../utils/zoom-out";
import { TEXTS } from "../../utils/constants/texts";
// Every section header the new default template must contain, in order.
const SECTION_HEADERS = [
"# Identity",
@ -158,7 +159,10 @@ test(
// Load Simple Agent — gives us a ready-to-run Agent wired to ChatInput/Output.
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Simple Agent" }).first().click();
await page
.getByRole("heading", { name: TEXTS.templateSimpleAgent })
.first()
.click();
await initialGPTsetup(page);
// Simple Agent's template may carry its own system_prompt — force the fresh
@ -181,7 +185,7 @@ test(
await page.getByTestId("input-chat-playground").last().fill("Run now.");
await page.getByTestId("button-send").last().click();
const stopButton = page.getByRole("button", { name: "Stop" });
const stopButton = page.getByRole("button", { name: TEXTS.stop });
await stopButton.waitFor({ state: "visible", timeout: 30000 });
await stopButton.waitFor({ state: "hidden", timeout: 120000 });
@ -220,7 +224,10 @@ test(
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Simple Agent" }).first().click();
await page
.getByRole("heading", { name: TEXTS.templateSimpleAgent })
.first()
.click();
await initialGPTsetup(page);
const customPrompt =
@ -238,7 +245,7 @@ test(
.fill("Introduce yourself in one short sentence.");
await page.getByTestId("button-send").last().click();
const stopButton = page.getByRole("button", { name: "Stop" });
const stopButton = page.getByRole("button", { name: TEXTS.stop });
await stopButton.waitFor({ state: "visible", timeout: 30000 });
await stopButton.waitFor({ state: "hidden", timeout: 120000 });
@ -280,7 +287,10 @@ test(
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Simple Agent" }).first().click();
await page
.getByRole("heading", { name: TEXTS.templateSimpleAgent })
.first()
.click();
await initialGPTsetup(page);
// Force the model to echo the date from its context — any obedience-style
@ -301,7 +311,7 @@ test(
.fill("What is today's date?");
await page.getByTestId("button-send").last().click();
const stopButton = page.getByRole("button", { name: "Stop" });
const stopButton = page.getByRole("button", { name: TEXTS.stop });
await stopButton.waitFor({ state: "visible", timeout: 30000 });
await stopButton.waitFor({ state: "hidden", timeout: 120000 });

View File

@ -12,6 +12,11 @@ import {
SNAPSHOTS_EMPTY_MOCK,
} from "../../utils/deployment-mocks";
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
// ---------------------------------------------------------------------------
// Helper: set up all required API route mocks
// ---------------------------------------------------------------------------
@ -225,11 +230,6 @@ test(
tag: ["@deployment", "@workspace"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await awaitBootstrapTest(page, { skipModal: true });
await setupDeploymentMocks(page, "");
await page.getByTestId("deployments-btn").click();
@ -250,11 +250,6 @@ test(
tag: ["@deployment", "@workspace"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await openDeploymentStepper(page);
// Next should be disabled before selection
@ -277,11 +272,6 @@ test(
tag: ["@deployment", "@workspace"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await openDeploymentStepper(page);
await selectProvider(page);
await page.getByTestId("deployment-stepper-next").click();
@ -318,11 +308,6 @@ test(
tag: ["@deployment", "@workspace"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await openDeploymentStepper(page);
await goToStepType(page);
@ -367,11 +352,6 @@ test(
tag: ["@deployment", "@workspace"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await openDeploymentStepper(page);
await goToStepReview(page);
@ -399,11 +379,6 @@ test(
tag: ["@deployment", "@workspace"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await openDeploymentStepper(page);
// Set up POST deployments mock (after bootstrap, before deploy click)
@ -454,11 +429,6 @@ test(
tag: ["@deployment", "@workspace"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await openDeploymentStepper(page);
await goToStepReview(page);
@ -493,11 +463,6 @@ test(
tag: ["@deployment", "@workspace"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await openDeploymentStepper(page, SNAPSHOTS_DUPLICATE_MOCK);
await goToStepReview(page);
@ -520,11 +485,6 @@ test(
tag: ["@deployment", "@workspace"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await openDeploymentStepper(page, SNAPSHOTS_EMPTY_MOCK);
await goToStepReview(page);
@ -547,11 +507,6 @@ test(
tag: ["@deployment", "@workspace"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await openDeploymentStepper(page, SNAPSHOTS_DUPLICATE_MOCK);
await goToStepReview(page);
@ -590,11 +545,6 @@ test(
tag: ["@deployment", "@workspace"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await openDeploymentStepper(page, SNAPSHOTS_EMPTY_MOCK, [
{ ...FLOWS_MOCK[0], name: "12925" },
]);

View File

@ -12,6 +12,11 @@ import {
PROVIDERS_MOCK,
} from "../../utils/deployment-mocks";
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
async function setupRoutes(page: Parameters<typeof test>[2]["page"]) {
// Register broad catch-all FIRST so specific routes (registered after) take priority via LIFO
await page.route("**/api/v1/deployments*", (route) => {
@ -101,11 +106,6 @@ test(
tag: ["@release", "@workspace", "@api"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await setupRoutes(page);
await navigateToDeployments(page);
@ -128,11 +128,6 @@ test(
tag: ["@release", "@workspace", "@api"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await setupRoutes(page);
await navigateToDeployments(page);
@ -161,11 +156,6 @@ test(
tag: ["@release", "@workspace", "@api"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await setupRoutes(page);
await navigateToDeployments(page);
@ -188,11 +178,6 @@ test(
tag: ["@release", "@workspace", "@api"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await setupRoutes(page);
await navigateToDeployments(page);
@ -233,11 +218,6 @@ test(
tag: ["@release", "@workspace", "@api"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await setupRoutes(page);
await navigateToDeployments(page);
@ -382,11 +362,6 @@ test(
tag: ["@release", "@workspace", "@api"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
// Capture folder ID from the projects API before bootstrap
const projectsResponsePromise = page.waitForResponse(
(resp) =>
@ -483,11 +458,6 @@ test(
tag: ["@release", "@workspace", "@api"],
},
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
const projectsResponsePromise = page.waitForResponse(
(resp) =>
resp.url().includes("/api/v1/projects") && resp.status() === 200,

View File

@ -6,6 +6,11 @@ import {
PROVIDERS_MOCK,
} from "../../utils/deployment-mocks";
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
async function navigateToProvidersTab(
page: Parameters<typeof test>[2]["page"],
) {
@ -19,11 +24,6 @@ test(
"Empty state shows Add Environment button",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await page.route("**/api/v1/deployments/providers*", (route) => {
route.fulfill({
status: 200,
@ -50,11 +50,6 @@ test(
"New Environment button opens modal with save disabled on empty form",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await page.route("**/api/v1/deployments/providers*", (route) => {
route.fulfill({
status: 200,
@ -84,11 +79,6 @@ test(
"Save button becomes enabled after filling all required fields",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await page.route("**/api/v1/deployments/providers*", (route) => {
route.fulfill({
status: 200,
@ -127,11 +117,6 @@ test(
"Save calls POST and modal closes",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
let postCalled = false;
await page.route("**/api/v1/deployments/providers*", (route) => {
@ -196,11 +181,6 @@ test(
"Delete triggers confirmation dialog then calls DELETE",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await page.route("**/api/v1/deployments*", (route) => {
route.fulfill({
status: 200,
@ -256,11 +236,6 @@ test(
"Cancel delete dismisses confirmation without calling DELETE",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
let deleteRequestCount = 0;
await page.route("**/api/v1/deployments*", (route) => {

View File

@ -9,6 +9,12 @@ import {
RUNNING_RUN_RESPONSE,
} from "../../utils/deployment-mocks";
import { TEXTS } from "../../utils/constants/texts";
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
async function setupBaseRoutes(page: Page) {
// Register broad catch-all FIRST so specific routes (registered after) take priority via LIFO
await page.route("**/api/v1/deployments*", (route) => {
@ -44,18 +50,13 @@ test(
"Test button opens modal with chat interface",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await setupBaseRoutes(page);
await navigateToDeploymentsPage(page);
await page.getByTestId("test-deployment-dep-1").click();
await expect(page.getByTestId("test-deployment-modal-title")).toBeVisible();
await expect(page.getByPlaceholder("Message")).toBeVisible();
await expect(page.getByPlaceholder(TEXTS.placeholderMessage)).toBeVisible();
},
);
@ -63,11 +64,6 @@ test(
"Send message calls POST run with correct body",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
let capturedRequestBody: Record<string, unknown> | null = null;
let runCallCount = 0;
@ -102,9 +98,9 @@ test(
await navigateToDeploymentsPage(page);
await page.getByTestId("test-deployment-dep-1").click();
await expect(page.getByPlaceholder("Message")).toBeVisible();
await expect(page.getByPlaceholder(TEXTS.placeholderMessage)).toBeVisible();
await page.getByPlaceholder("Message").fill("Hello AI");
await page.getByPlaceholder(TEXTS.placeholderMessage).fill("Hello AI");
await page.getByRole("button", { name: /send message/i }).click();
await expect
@ -119,11 +115,6 @@ test(
"Response appears in chat after polling completes",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
let runCallCount = 0;
await setupBaseRoutes(page);
@ -155,12 +146,12 @@ test(
await navigateToDeploymentsPage(page);
await page.getByTestId("test-deployment-dep-1").click();
await expect(page.getByPlaceholder("Message")).toBeVisible();
await expect(page.getByPlaceholder(TEXTS.placeholderMessage)).toBeVisible();
await page.getByPlaceholder("Message").fill("Hello AI");
await page.getByPlaceholder(TEXTS.placeholderMessage).fill("Hello AI");
await page.getByRole("button", { name: /send message/i }).click();
await expect(page.getByText("Hello from AI")).toBeVisible({
await expect(page.getByText(TEXTS.labelHelloFromAi)).toBeVisible({
timeout: 30_000,
});
expect(runCallCount).toBeGreaterThanOrEqual(2);
@ -171,11 +162,6 @@ test(
"Input is disabled during polling and re-enabled after response",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
let runCallCount = 0;
await setupBaseRoutes(page);
@ -207,19 +193,21 @@ test(
await navigateToDeploymentsPage(page);
await page.getByTestId("test-deployment-dep-1").click();
await expect(page.getByPlaceholder("Message")).toBeVisible();
await expect(page.getByPlaceholder(TEXTS.placeholderMessage)).toBeVisible();
await page.getByPlaceholder("Message").fill("Hello AI");
await page.getByPlaceholder(TEXTS.placeholderMessage).fill("Hello AI");
await page.getByRole("button", { name: /send message/i }).click();
// Textarea should be disabled while waiting for response
await expect(page.getByPlaceholder("Message")).toBeDisabled();
await expect(
page.getByPlaceholder(TEXTS.placeholderMessage),
).toBeDisabled();
// After response arrives, textarea should be re-enabled
await expect(page.getByText("Hello from AI")).toBeVisible({
await expect(page.getByText(TEXTS.labelHelloFromAi)).toBeVisible({
timeout: 30_000,
});
await expect(page.getByPlaceholder("Message")).toBeEnabled();
await expect(page.getByPlaceholder(TEXTS.placeholderMessage)).toBeEnabled();
},
);
@ -227,11 +215,6 @@ test(
"Multi-turn: second message includes thread_id from first response",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
let runCallCount = 0;
const capturedBodies: Array<Record<string, unknown>> = [];
@ -268,23 +251,25 @@ test(
await navigateToDeploymentsPage(page);
await page.getByTestId("test-deployment-dep-1").click();
await expect(page.getByPlaceholder("Message")).toBeVisible();
await expect(page.getByPlaceholder(TEXTS.placeholderMessage)).toBeVisible();
// First message
await page.getByPlaceholder("Message").fill("First message");
await page.getByPlaceholder(TEXTS.placeholderMessage).fill("First message");
await page.getByRole("button", { name: /send message/i }).click();
// Wait for first response
await expect(page.getByText("Hello from AI")).toBeVisible({
await expect(page.getByText(TEXTS.labelHelloFromAi)).toBeVisible({
timeout: 30_000,
});
// Second message
await page.getByPlaceholder("Message").fill("Second message");
await page
.getByPlaceholder(TEXTS.placeholderMessage)
.fill("Second message");
await page.getByRole("button", { name: /send message/i }).click();
// Wait for second response
await expect(page.getByText("Hello from AI")).toHaveCount(2, {
await expect(page.getByText(TEXTS.labelHelloFromAi)).toHaveCount(2, {
timeout: 30_000,
});
@ -303,11 +288,6 @@ test(
"Close and reopen modal resets chat history",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
let runCallCount = 0;
await setupBaseRoutes(page);
@ -340,13 +320,13 @@ test(
// Open modal and send a message
await page.getByTestId("test-deployment-dep-1").click();
await expect(page.getByPlaceholder("Message")).toBeVisible();
await expect(page.getByPlaceholder(TEXTS.placeholderMessage)).toBeVisible();
await page.getByPlaceholder("Message").fill("Hello AI");
await page.getByPlaceholder(TEXTS.placeholderMessage).fill("Hello AI");
await page.getByRole("button", { name: /send message/i }).click();
// Wait for response to appear
await expect(page.getByText("Hello from AI")).toBeVisible({
await expect(page.getByText(TEXTS.labelHelloFromAi)).toBeVisible({
timeout: 30_000,
});
@ -354,16 +334,18 @@ test(
await page.keyboard.press("Escape");
// Verify the modal is closed
await expect(page.getByPlaceholder("Message")).not.toBeVisible();
await expect(
page.getByPlaceholder(TEXTS.placeholderMessage),
).not.toBeVisible();
// Reopen the modal
runCallCount = 0;
await page.getByTestId("test-deployment-dep-1").click();
await expect(page.getByPlaceholder("Message")).toBeVisible();
await expect(page.getByPlaceholder(TEXTS.placeholderMessage)).toBeVisible();
// Chat should be empty — no previous messages visible
await expect(page.getByText("Hello AI")).not.toBeVisible();
await expect(page.getByText("Hello from AI")).not.toBeVisible();
await expect(page.getByText(TEXTS.labelHelloFromAi)).not.toBeVisible();
// Empty state ("Agent Chat") should be shown
await expect(page.getByText("Agent Chat")).toBeVisible();

View File

@ -6,6 +6,11 @@ import {
PROVIDERS_MOCK,
} from "../../utils/deployment-mocks";
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
async function navigateToDeploymentsTab(
page: Parameters<typeof test>[2]["page"],
) {
@ -18,11 +23,6 @@ test(
"Renders Deployments tab with sub-tab toggles",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await page.route("**/api/v1/deployments/providers*", (route) => {
route.fulfill({
status: 200,
@ -52,11 +52,6 @@ test(
"Deployments empty state shows create button when no providers exist",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await page.route("**/api/v1/deployments/providers*", (route) => {
route.fulfill({
status: 200,
@ -85,11 +80,6 @@ test(
"Providers empty state shows add provider button when switching to providers tab",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await page.route("**/api/v1/deployments/providers*", (route) => {
route.fulfill({
status: 200,
@ -120,11 +110,6 @@ test(
"Deployment list renders a row for each deployment",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await page.route("**/api/v1/deployments*", (route) => {
route.fulfill({
status: 200,
@ -153,11 +138,6 @@ test(
"Provider list renders a row for each provider",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await page.route("**/api/v1/deployments*", (route) => {
route.fulfill({
status: 200,
@ -188,11 +168,6 @@ test(
"Delete deployment opens type-to-confirm dialog",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await page.route("**/api/v1/deployments/providers*", (route) => {
route.fulfill({
status: 200,
@ -225,11 +200,6 @@ test(
"Delete deployment button is disabled until deployment name is typed",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await page.route("**/api/v1/deployments/providers*", (route) => {
route.fulfill({
status: 200,
@ -267,11 +237,6 @@ test(
"Delete deployment — typing correct name and confirming calls DELETE",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
await page.route("**/api/v1/deployments/providers*", (route) => {
route.fulfill({
status: 200,
@ -320,11 +285,6 @@ test(
"Cancel delete deployment dismisses dialog without calling DELETE",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
let deleteRequestCount = 0;
await page.route("**/api/v1/deployments/providers*", (route) => {
@ -379,11 +339,6 @@ test(
"Names filter is passed to API when fetching deployments",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
process.env.LANGFLOW_FEATURE_WXO_DEPLOYMENTS !== "true",
"Requires LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true",
);
let resolveNamesRequest: ((url: string) => void) | undefined;
const namesRequest = new Promise<string>((resolve) => {
resolveNamesRequest = resolve;

View File

@ -1,6 +1,7 @@
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
test.describe("Flow Lock Feature", () => {
test(
"should lock and unlock a flow and verify UI changes",
@ -10,7 +11,9 @@ test.describe("Flow Lock Feature", () => {
// Navigate to templates and select a flow to work with
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 5000,
@ -120,7 +123,9 @@ test.describe("Flow Lock Feature", () => {
// Navigate to templates and select a flow
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 5000,

View File

@ -1,6 +1,7 @@
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
/**
* Tests for folder deletion integrity
*
@ -18,7 +19,9 @@ test(
// Navigate to templates and create a flow first
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 30000,
@ -36,14 +39,14 @@ test(
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.waitFor({ state: "visible", timeout: 10000 });
// Rename the folder for easier identification
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.dblclick();
@ -71,10 +74,10 @@ test(
.waitFor({ state: "visible", timeout: 5000 });
await page.getByTestId("more-options-button_test-folder-to-delete").click();
await page.getByTestId("btn-delete-project").click();
await page.getByText("Delete").last().click();
await page.getByText(TEXTS.delete).last().click();
// Verify success message
await expect(page.getByText("Project deleted successfully")).toBeVisible({
await expect(page.getByText(TEXTS.toastProjectDeleted)).toBeVisible({
timeout: 5000,
});
@ -98,7 +101,9 @@ test(
// Navigate to templates
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 30000,
@ -116,13 +121,13 @@ test(
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.waitFor({ state: "visible", timeout: 10000 });
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.dblclick();
@ -141,13 +146,13 @@ test(
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.waitFor({ state: "visible", timeout: 10000 });
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.dblclick();
@ -177,10 +182,10 @@ test(
.waitFor({ state: "visible", timeout: 5000 });
await page.getByTestId("more-options-button_folder-alpha").click();
await page.getByTestId("btn-delete-project").click();
await page.getByText("Delete").last().click();
await page.getByText(TEXTS.delete).last().click();
// Verify success message
await expect(page.getByText("Project deleted successfully")).toBeVisible({
await expect(page.getByText(TEXTS.toastProjectDeleted)).toBeVisible({
timeout: 5000,
});
@ -208,9 +213,9 @@ test(
.waitFor({ state: "visible", timeout: 5000 });
await page.getByTestId("more-options-button_folder-beta").click();
await page.getByTestId("btn-delete-project").click();
await page.getByText("Delete").last().click();
await page.getByText(TEXTS.delete).last().click();
await expect(page.getByText("Project deleted successfully")).toBeVisible({
await expect(page.getByText(TEXTS.toastProjectDeleted)).toBeVisible({
timeout: 5000,
});
},
@ -224,7 +229,9 @@ test(
// Navigate to templates
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 30000,
@ -242,13 +249,13 @@ test(
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.waitFor({ state: "visible", timeout: 10000 });
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.dblclick();
@ -270,9 +277,9 @@ test(
.waitFor({ state: "visible", timeout: 5000 });
await page.getByTestId("more-options-button_folder-one").click();
await page.getByTestId("btn-delete-project").click();
await page.getByText("Delete").last().click();
await page.getByText(TEXTS.delete).last().click();
await expect(page.getByText("Project deleted successfully")).toBeVisible({
await expect(page.getByText(TEXTS.toastProjectDeleted)).toBeVisible({
timeout: 5000,
});
@ -286,13 +293,13 @@ test(
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.waitFor({ state: "visible", timeout: 10000 });
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.dblclick();
@ -317,9 +324,9 @@ test(
.waitFor({ state: "visible", timeout: 5000 });
await page.getByTestId("more-options-button_folder-two").click();
await page.getByTestId("btn-delete-project").click();
await page.getByText("Delete").last().click();
await page.getByText(TEXTS.delete).last().click();
await expect(page.getByText("Project deleted successfully")).toBeVisible({
await expect(page.getByText(TEXTS.toastProjectDeleted)).toBeVisible({
timeout: 5000,
});
},
@ -378,10 +385,10 @@ test(
}
await page.getByTestId("btn-delete-project").click();
await page.getByText("Delete").last().click();
await page.getByText(TEXTS.delete).last().click();
// Wait for deletion to complete
await expect(page.getByText("Project deleted successfully")).toBeVisible({
await expect(page.getByText(TEXTS.toastProjectDeleted)).toBeVisible({
timeout: 5000,
});
@ -403,9 +410,32 @@ test(
await page.getByTestId("new_project_btn_empty_page").click();
// The empty-state CTA can either open the templates modal directly
// (EmptyPageCommunity) or surface the FlowBuilderWelcome overlay
// (EmptyFolder → useStartNewFlow). Race both and click "Browse more"
// when the overlay shows up so we always end up on the templates modal.
await Promise.race([
page.waitForSelector('[data-testid="modal-title"]', { timeout: 30000 }),
page.waitForSelector('[data-testid="flow-builder-welcome-panel"]', {
timeout: 30000,
}),
]);
if (
(await page
.locator('[data-testid="flow-builder-welcome-panel"]')
.count()) > 0
) {
await page.getByTestId("flow-builder-welcome-browse-more").click();
await page.waitForSelector('[data-testid="modal-title"]', {
timeout: 30000,
});
}
// Navigate to templates
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 30000,
@ -422,8 +452,10 @@ test(
// Verify we can click on the folder and see the flow
await page.getByTestId("sidebar-nav-Starter Project").click();
// The folder should contain our newly created flow
await expect(page.getByTestId("list-card")).toBeVisible({
// The folder should contain our newly created flow. Templates render an
// extra example list-card alongside the user's flow when the folder is
// freshly created, so scope to the first match to satisfy strict mode.
await expect(page.getByTestId("list-card").first()).toBeVisible({
timeout: 5000,
});
},

View File

@ -3,6 +3,7 @@ import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { renameFlow } from "../../utils/rename-flow";
import { TEXTS } from "../../utils/constants/texts";
test(
"CRUD folders",
{ tag: ["@release", "@api"] },
@ -11,33 +12,32 @@ test(
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 100000,
});
await page.getByTestId("icon-ChevronLeft").first().click();
await page.getByPlaceholder("Search flows").first().isVisible();
await page.getByText("Flows").first().isVisible();
if (await page.getByText("Components").first().isVisible()) {
await page.getByText("Components").first().isVisible();
await expect(page.getByPlaceholder("Search flows").first()).toBeVisible();
await expect(page.getByText("Flows").first()).toBeVisible();
if (await page.getByText(TEXTS.labelComponents).first().isVisible()) {
await expect(page.getByText(TEXTS.labelComponents).first()).toBeVisible();
} else {
await page.getByText("MCP Server").first().isVisible();
await expect(page.getByText("MCP Server").first()).toBeVisible();
}
await page.getByText("All").first().isVisible();
await page.getByText("Select All").first().isVisible();
await page.getByTestId("add-project-button").click();
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.isVisible();
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.dblclick();
@ -62,8 +62,8 @@ test(
await page.getByTestId("more-options-button_new-project-test-name").click();
await page.getByTestId("btn-delete-project").click();
await page.getByText("Delete").last().click();
await expect(page.getByText("Project deleted successfully")).toBeVisible({
await page.getByText(TEXTS.delete).last().click();
await expect(page.getByText(TEXTS.toastProjectDeleted)).toBeVisible({
timeout: 3000,
});
},
@ -138,7 +138,9 @@ test("change flow folder", async ({ page }) => {
// unique so our assertions can't collide with any template that
// Starter ships with by default.
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 100000,
@ -155,12 +157,12 @@ test("change flow folder", async ({ page }) => {
await page.getByTestId("add-project-button").click();
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.waitFor({ state: "visible", timeout: 10000 });
await page
.locator("[data-testid='project-sidebar']")
.getByText("New Project")
.getByText(TEXTS.labelNewProject)
.last()
.dblclick();
await page.getByTestId("input-project").fill(destinationProjectName);

View File

@ -6,6 +6,7 @@ import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { TEXTS } from "../../utils/constants/texts";
test(
"user must be able to freeze a path",
{ tag: ["@release", "@workspace", "@components"] },
@ -31,7 +32,9 @@ test(
}
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await initialGPTsetup(page);
@ -40,7 +43,7 @@ test(
const randomSeed1 = Math.random().toString(36).substring(2, 10);
const randomSeed2 = Math.random().toString(36).substring(2, 10);
await page.getByText("Chat Input", { exact: true }).click();
await page.getByText(TEXTS.componentChatInput, { exact: true }).click();
await page
.getByTestId("textarea_str_input_value")
@ -57,7 +60,7 @@ test(
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", {
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 60000,
});
@ -67,13 +70,13 @@ test(
.click();
const randomTextGeneratedByAI = await page
.getByPlaceholder("Empty")
.getByPlaceholder(TEXTS.placeholderEmpty)
.first()
.inputValue();
await page.getByText("Close").last().click();
await page.getByText(TEXTS.close).last().click();
await page.getByText("Chat Input", { exact: true }).click();
await page.getByText(TEXTS.componentChatInput, { exact: true }).click();
// Use a completely different prompt to ensure different output
await page
@ -88,7 +91,7 @@ test(
});
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", {
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 60000,
});
@ -98,15 +101,15 @@ test(
.click();
const secondRandomTextGeneratedByAI = await page
.getByPlaceholder("Empty")
.getByPlaceholder(TEXTS.placeholderEmpty)
.first()
.inputValue();
await page.getByText("Close").last().click();
await page.getByText(TEXTS.close).last().click();
const languageModelNode = page
.locator(".react-flow__node", {
has: page.getByText("Language Model", { exact: true }),
has: page.getByText(TEXTS.componentLanguageModel, { exact: true }),
})
.last();
@ -133,7 +136,7 @@ test(
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", {
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 60000,
});
@ -143,11 +146,11 @@ test(
.click();
const thirdRandomTextGeneratedByAI = await page
.getByPlaceholder("Empty")
.getByPlaceholder(TEXTS.placeholderEmpty)
.first()
.inputValue();
await page.getByText("Close").last().click();
await page.getByText(TEXTS.close).last().click();
// The frozen path should return the cached (second) result, not generate new output
expect(secondRandomTextGeneratedByAI).toEqual(thirdRandomTextGeneratedByAI);

View File

@ -4,6 +4,7 @@ import { addLegacyComponents } from "../../utils/add-legacy-components";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
test(
"user must be able to freeze a component",
{ tag: ["@release", "@workspace", "@components"] },
@ -24,7 +25,7 @@ test(
await addLegacyComponents(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("text input");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchTextInput);
await page.waitForSelector('[data-testid="input_outputText Input"]', {
timeout: 1000,
});
@ -45,11 +46,13 @@ test(
await page.getByTestId("output-inspection-output text-textinput").click();
const firstOutputText = await page.getByPlaceholder("Empty").textContent();
const firstOutputText = await page
.getByPlaceholder(TEXTS.placeholderEmpty)
.textContent();
expect(firstOutputText).toBe("hello world");
await page.getByText("Close").last().click();
await page.getByText(TEXTS.close).last().click();
await page.getByTestId("textarea_str_input_value").fill("goodbye world");
@ -76,7 +79,9 @@ test(
await page.getByTestId("output-inspection-output text-textinput").click();
const secondOutputText = await page.getByPlaceholder("Empty").textContent();
const secondOutputText = await page
.getByPlaceholder(TEXTS.placeholderEmpty)
.textContent();
expect(secondOutputText).toBe("goodbye world");
},

View File

@ -5,6 +5,7 @@ import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { zoomOut } from "../../utils/zoom-out";
import { TEXTS } from "../../utils/constants/texts";
test(
"user must be able to save or delete a global variable",
{ tag: ["@release", "@workspace", "@api"] },
@ -17,7 +18,9 @@ test(
});
await page.getByTestId("blank-flow").click();
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("openai");
await page
.getByTestId("sidebar-search-input")
.fill(TEXTS.providerOpenAiSearch);
await page.waitForSelector('[data-testid="openaiOpenAI"]', {
timeout: 1000,
@ -46,7 +49,7 @@ test(
await page.getByTestId("icon-Globe").nth(0).click();
await page.getByText("Add New Variable", { exact: true }).click();
await page
.getByPlaceholder("Enter a name for the variable...")
.getByPlaceholder(TEXTS.placeholderVariableName)
.fill(genericName);
await page.getByText("Generic", { exact: true }).first().isVisible();
await page
@ -58,7 +61,7 @@ test(
await page.getByText("Add New Variable", { exact: true }).click();
await page
.getByPlaceholder("Enter a name for the variable...")
.getByPlaceholder(TEXTS.placeholderVariableName)
.fill(credentialName);
await page.getByTestId("credential-tab").click();
await page
@ -73,7 +76,7 @@ test(
.hover()
.then(async () => {
await page.getByTestId("icon-Trash2").last().click();
await page.getByText("Delete", { exact: true }).nth(1).click();
await page.getByText(TEXTS.delete, { exact: true }).nth(1).click();
});
},
);

View File

@ -1,41 +0,0 @@
import { test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
test.describe("group node test", () => {
/// <reference lib="dom"/>
// TODO: fix this test
test.skip(
"group and ungroup updating values",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page
.getByRole("heading", { name: "Basic Prompting" })
.first()
.click();
await page.getByTestId("fit_view").first().click();
await page.getByTestId("title-OpenAI").click();
await page
.getByTestId("title-OpenAI")
.click({ modifiers: ["ControlOrMeta"] });
await page
.getByTestId("title-Prompt Template")
.click({ modifiers: ["ControlOrMeta"] });
await page
.getByTestId("title-OpenAI")
.click({ modifiers: ["ControlOrMeta"] });
await page.getByRole("button", { name: "Group" }).click();
await page.getByTestId("title-Group").click();
await page.getByTestId("edit-name-description-button").click();
await page.getByTestId("input-title-Group").first().fill("test");
await page.getByTestId("save-name-description-button").first().click();
await page.keyboard.press("ControlOrMeta+g");
await page.getByTestId("title-OpenAI").isVisible();
await page.getByTestId("title-Prompt Template").isVisible();
},
);
});

View File

@ -1,6 +1,7 @@
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
test.describe("ModelInputComponent", () => {
test(
"should display model selector in a node with model input",
@ -16,7 +17,7 @@ test.describe("ModelInputComponent", () => {
// Search for OpenAI component which has model input
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("OpenAI");
await page.getByTestId("sidebar-search-input").fill(TEXTS.providerOpenAi);
// Wait for search results
await page.waitForTimeout(500);
@ -54,7 +55,7 @@ test.describe("ModelInputComponent", () => {
});
// Add an OpenAI model node
await page.getByTestId("sidebar-search-input").fill("OpenAI");
await page.getByTestId("sidebar-search-input").fill(TEXTS.providerOpenAi);
await page.waitForTimeout(500);
const openaiComponent = page.getByTestId("modelsOpenAI").first();
@ -93,7 +94,7 @@ test.describe("ModelInputComponent", () => {
});
// Add a model component
await page.getByTestId("sidebar-search-input").fill("OpenAI");
await page.getByTestId("sidebar-search-input").fill(TEXTS.providerOpenAi);
await page.waitForTimeout(500);
const openaiComponent = page.getByTestId("modelsOpenAI").first();
@ -133,7 +134,7 @@ test.describe("ModelInputComponent", () => {
});
// Add a model component
await page.getByTestId("sidebar-search-input").fill("OpenAI");
await page.getByTestId("sidebar-search-input").fill(TEXTS.providerOpenAi);
await page.waitForTimeout(500);
const openaiComponent = page.getByTestId("modelsOpenAI").first();
@ -175,7 +176,7 @@ test.describe("ModelInputComponent", () => {
});
// Add a model component
await page.getByTestId("sidebar-search-input").fill("OpenAI");
await page.getByTestId("sidebar-search-input").fill(TEXTS.providerOpenAi);
await page.waitForTimeout(500);
const openaiComponent = page.getByTestId("modelsOpenAI").first();
@ -212,7 +213,7 @@ test.describe("ModelInputComponent", () => {
});
// Add a model component
await page.getByTestId("sidebar-search-input").fill("OpenAI");
await page.getByTestId("sidebar-search-input").fill(TEXTS.providerOpenAi);
await page.waitForTimeout(500);
const openaiComponent = page.getByTestId("modelsOpenAI").first();

View File

@ -1,184 +0,0 @@
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
test.describe("ModelProviderCount Component", () => {
test(
"should open model provider page when navigating via settings",
{ tag: ["@release", "@components", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
// Close the new project modal
const closeButton = page
.locator("button")
.filter({ hasText: "Close" })
.or(page.locator('button[aria-label="Close"]'))
.or(page.locator('button[data-testid="close-button"]'))
.first();
await closeButton.click();
// Navigate to settings > Model Providers
await page.getByTestId("user-profile-settings").click();
await page.getByText("Settings").first().click();
await page.getByText("Model Providers").first().click();
// Wait for the settings page header
await page.waitForSelector('[data-testid="settings_menu_header"]', {
timeout: 5000,
});
// Page should appear with "Model Providers" header
await expect(
page.getByTestId("settings_menu_header").last(),
).toContainText("Model Providers", { timeout: 5000 });
// Page should contain provider configuration content
await expect(
page.getByText(
"Configure AI model providers and manage their API keys.",
),
).toBeVisible();
},
);
test(
"should navigate back from model provider page",
{ tag: ["@release", "@components", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
const closeButton = page
.locator("button")
.filter({ hasText: "Close" })
.or(page.locator('button[aria-label="Close"]'))
.or(page.locator('button[data-testid="close-button"]'))
.first();
await closeButton.click();
// Navigate to settings > Model Providers
await page.getByTestId("user-profile-settings").click();
await page.getByText("Settings").first().click();
await page.getByText("Model Providers").first().click();
// Verify page is open
await expect(
page.getByTestId("settings_menu_header").last(),
).toContainText("Model Providers", { timeout: 5000 });
// Navigate back by clicking the back button
await page.getByTestId("icon-ChevronLeft").first().click();
// Should be back at main view - the settings page header should change
await expect(
page.getByText(
"Configure AI model providers and manage their API keys.",
),
).not.toBeVisible({ timeout: 3000 });
},
);
test(
"should navigate to model provider page multiple times",
{ tag: ["@release", "@components", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
const closeButton = page
.locator("button")
.filter({ hasText: "Close" })
.or(page.locator('button[aria-label="Close"]'))
.or(page.locator('button[data-testid="close-button"]'))
.first();
await closeButton.click();
// First navigation - open page
await page.getByTestId("user-profile-settings").click();
await page.getByText("Settings").first().click();
await page.getByText("Model Providers").first().click();
await expect(
page.getByTestId("settings_menu_header").last(),
).toContainText("Model Providers", { timeout: 5000 });
// Navigate back
await page.getByTestId("icon-ChevronLeft").first().click();
await expect(
page.getByText(
"Configure AI model providers and manage their API keys.",
),
).not.toBeVisible({ timeout: 3000 });
// Second navigation - open page again
await page.getByTestId("user-profile-settings").click();
await page.getByText("Settings").first().click();
await page.getByText("Model Providers").first().click();
await expect(
page.getByTestId("settings_menu_header").last(),
).toContainText("Model Providers", { timeout: 5000 });
},
);
test(
"should display provider list in the page",
{ tag: ["@release", "@components", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
const closeButton = page
.locator("button")
.filter({ hasText: "Close" })
.or(page.locator('button[aria-label="Close"]'))
.or(page.locator('button[data-testid="close-button"]'))
.first();
await closeButton.click();
// Navigate to settings > Model Providers
await page.getByTestId("user-profile-settings").click();
await page.getByText("Settings").first().click();
await page.getByText("Model Providers").first().click();
// Wait for page to be fully visible
await expect(
page.getByTestId("settings_menu_header").last(),
).toContainText("Model Providers", { timeout: 5000 });
// The page should contain provider selection area with content
await page.waitForTimeout(500);
// Page should be properly structured with content
await expect(page.locator("div").first()).toBeVisible();
},
);
test(
"model provider page should display provider count information",
{ tag: ["@release", "@components", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
const closeButton = page
.locator("button")
.filter({ hasText: "Close" })
.or(page.locator('button[aria-label="Close"]'))
.or(page.locator('button[data-testid="close-button"]'))
.first();
await closeButton.click();
// Navigate to settings > Model Providers
await page.getByTestId("user-profile-settings").click();
await page.getByText("Settings").first().click();
await page.getByText("Model Providers").first().click();
// Page should display the header
await expect(
page.getByTestId("settings_menu_header").last(),
).toContainText("Model Providers", { timeout: 5000 });
// Provider list content should be visible
const providerList = page.getByTestId("provider-list");
if (await providerList.isVisible({ timeout: 3000 })) {
await expect(providerList).toBeVisible();
}
},
);
});

View File

@ -178,4 +178,35 @@ test.describe("ModelProviderModal", () => {
).toBeVisible();
},
);
test(
"should navigate to model provider page multiple times",
{ tag: ["@release", "@components", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page, { skipModal: true });
// Wait for page to be ready
await page.waitForTimeout(1000);
// First navigation - open page
await navigateSettingsPages(page, "Settings", "Model Providers");
await expect(
page.getByTestId("settings_menu_header").last(),
).toContainText("Model Providers", { timeout: 5000 });
// Navigate back
await page.getByTestId("icon-ChevronLeft").first().click();
await expect(
page.getByText(
"Configure AI model providers and manage their API keys.",
),
).not.toBeVisible({ timeout: 3000 });
// Second navigation - open page again
await navigateSettingsPages(page, "Settings", "Model Providers");
await expect(
page.getByTestId("settings_menu_header").last(),
).toContainText("Model Providers", { timeout: 5000 });
},
);
});

View File

@ -1,68 +1,45 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { zoomOut } from "../../utils/zoom-out";
import { TID } from "../../utils/constants/testIds";
import { TIMEOUTS } from "../../utils/constants/timeouts";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { addComponentFromSidebar } from "../../utils/flow/add-component-from-sidebar";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
import { disableInspectPanel } from "../../utils/open-advanced-options";
import { sessionMoreMenu } from "../../utils/playground/sessions";
import { zoomOut } from "../../utils/zoom-out";
import { TEXTS } from "../../utils/constants/texts";
test(
"fresh start playground",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
loadDotenvIfLocal(__dirname);
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await disableInspectPanel(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat output");
await page.waitForSelector('[data-testid="input_outputChat Output"]', {
timeout: 100000,
await addComponentFromSidebar(page, {
search: "chat output",
testId: "input_outputChat Output",
hoverAdd: true,
});
await page
.getByTestId("input_outputChat Output")
.hover()
.then(async () => {
await page.getByTestId("add-component-button-chat-output").click();
});
await zoomOut(page, 2);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat input");
await page.waitForSelector('[data-testid="input_outputChat Input"]', {
timeout: 100000,
await addComponentFromSidebar(page, {
search: "chat input",
testId: "input_outputChat Input",
position: { x: 100, y: 100 },
});
await page
.getByTestId("input_outputChat Input")
.dragTo(page.locator('//*[@id="react-flow-id"]'), {
targetPosition: { x: 100, y: 100 },
});
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("text output");
await page.waitForSelector('[data-testid="input_outputText Output"]', {
timeout: 100000,
await addComponentFromSidebar(page, {
search: "text output",
testId: "input_outputText Output",
position: { x: 300, y: 300 },
});
await page
.getByTestId("input_outputText Output")
.dragTo(page.locator('//*[@id="react-flow-id"]'), {
targetPosition: { x: 300, y: 300 },
});
await adjustScreenView(page);
await page
@ -78,14 +55,16 @@ test(
.getByTestId("handle-chatoutput-noshownode-inputs-target")
.click();
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page.waitForSelector('[data-testid="input-chat-playground"]', {
timeout: 100000,
await page
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForSelector(`[data-testid="${TID.inputChatPlayground}"]`, {
timeout: TIMEOUTS.componentMount,
});
//send message
await page.getByTestId("input-chat-playground").click();
await page.getByTestId("input-chat-playground").fill("message 1");
await page.getByTestId(TID.inputChatPlayground).click();
await page.getByTestId(TID.inputChatPlayground).fill("message 1");
await page.keyboard.press("Enter");
await expect(page.getByTestId("chat-message-User-message 1")).toBeVisible();
await expect(page.getByTestId("chat-message-AI-message 1")).toBeVisible();
@ -119,63 +98,57 @@ test(
await expect(page.getByTestId("chat-message-AI-edit_bot_1")).toBeVisible();
// check table messages view (use sidebar session more menu — header menu hidden in fullscreen)
await page
.locator('[data-testid^="session-"][data-testid$="-more-menu"]')
.first()
.click();
await sessionMoreMenu(page, "first").click();
await page.getByTestId("message-logs-option").click();
await expect(page.getByText("Page 1 of 1", { exact: true })).toBeVisible();
await page.getByRole("button", { name: "Close" }).click();
await page.getByRole("button", { name: TEXTS.close }).click();
// create new session (use sidebar new-chat button)
await page.getByTestId("new-chat").click();
await page.getByTestId(TID.newChat).click();
await expect(page.getByTitle("New Session 0")).toBeVisible();
// check rename session
await page
.getByTestId("input-chat-playground")
.getByTestId(TID.inputChatPlayground)
.fill("session_after_delete");
await page.keyboard.press("Enter");
await page
.getByTestId("chat-message-User-session_after_delete")
.isVisible();
// Use sidebar session more menu for rename
await page
.locator('[data-testid^="session-"][data-testid$="-more-menu"]')
.last()
.click();
await sessionMoreMenu(page, "last").click();
await page.getByTestId("rename-session-option").click();
await page.getByTestId("session-rename-input").fill("my first session");
await page.keyboard.press("Enter");
await expect(
page.getByTestId("session-selector").getByText("my first session"),
).toBeVisible();
page
.getByTestId(TID.sessionSelector)
.filter({ hasText: "my first session" })
.first(),
).toBeVisible({ timeout: 10000 });
// check cancel rename (using Escape key)
await page
.locator('[data-testid^="session-"][data-testid$="-more-menu"]')
.last()
.click();
await sessionMoreMenu(page, "last").click();
await page.getByTestId("rename-session-option").click();
await page.getByTestId("session-rename-input").fill("cancel name");
await page.keyboard.press("Escape");
await expect(
page.getByTestId("session-selector").getByText("my first session"),
).toBeVisible();
page
.getByTestId(TID.sessionSelector)
.filter({ hasText: "my first session" })
.first(),
).toBeVisible({ timeout: 10000 });
// check delete session
await page
.locator('[data-testid^="session-"][data-testid$="-more-menu"]')
.last()
.click();
await sessionMoreMenu(page, "last").click();
await page.getByTestId("delete-session-option").click();
await expect(page.getByTitle("Default Session")).toBeVisible();
//create new session
await page.getByTestId("new-chat").click();
await page.getByTestId("input-chat-playground").click();
await page.getByTestId(TID.newChat).click();
await page.getByTestId(TID.inputChatPlayground).click();
await page
.getByTestId("input-chat-playground")
.getByTestId(TID.inputChatPlayground)
.fill("session_after_delete");
await page.keyboard.press("Enter");
await expect(

View File

@ -1,107 +1,85 @@
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { ANIMATIONS, TIMEOUTS } from "../../utils/constants/timeouts";
import { TID } from "../../utils/constants/testIds";
import { addComponentFromSidebar } from "../../utils/flow/add-component-from-sidebar";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
import { TEXTS } from "../../utils/constants/texts";
test(
"user should be able to publish a flow",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page, context }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 5000,
await openBlankFlow(page);
await page.waitForSelector(`[data-testid="${TID.sidebarSearchInput}"]`, {
timeout: TIMEOUTS.short,
});
await page.getByTestId("blank-flow").click();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 5000,
await addComponentFromSidebar(page, {
search: "chat input",
testId: "input_outputChat Input",
hoverAdd: true,
});
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat input");
await page.waitForSelector('[data-testid="input_outputChat Input"]', {
timeout: 3000,
});
await page
.getByTestId("input_outputChat Input")
.hover({ timeout: 3000 })
.then(async () => {
await page
.getByTestId("add-component-button-chat-input")
.last()
.click();
});
await page.waitForTimeout(2000);
await adjustScreenView(page, { numberOfZoomOut: 3 });
await page.getByTestId("publish-button").click();
await page.getByTestId(TID.publishButton).click();
await page.waitForTimeout(3000);
await page.waitForSelector('[data-testid="shareable-playground"]', {
timeout: 10000,
await page.waitForSelector(`[data-testid="${TID.shareablePlayground}"]`, {
timeout: TIMEOUTS.medium,
});
try {
await page.waitForTimeout(2000);
await expect(page.getByTestId(TID.publishSwitch)).toBeVisible({
timeout: TIMEOUTS.medium,
});
await expect(page.getByTestId("publish-switch")).toBeVisible({
timeout: 10000,
});
} catch (error) {
console.error("Error waiting for publish operation:", error);
throw error;
}
await page.waitForTimeout(2000);
await page.getByTestId("publish-switch").click();
await page.getByTestId(TID.publishSwitch).click();
const pagePromise = context.waitForEvent("page");
await page.waitForTimeout(ANIMATIONS.publishTogglePropagation);
await page.waitForTimeout(2000);
await page.getByTestId("shareable-playground").click();
await page.getByTestId(TID.shareablePlayground).click();
const newPage = await pagePromise;
await newPage.waitForLoadState("domcontentloaded");
// Wait for the chat input to actually be present before filling. The
// default actionTimeout (20s) was not enough on Windows CI for the
// shareable-playground page to mount the message input.
await newPage
.getByPlaceholder("Send a message...")
.waitFor({ state: "visible", timeout: 60000 });
.getByPlaceholder(TEXTS.placeholderSendMessage)
.waitFor({ state: "visible", timeout: TIMEOUTS.long });
const newUrl = newPage.url();
await newPage.getByPlaceholder("Send a message...").fill("Hello");
await newPage.getByTestId("button-send").last().click();
await newPage.getByPlaceholder(TEXTS.placeholderSendMessage).fill("Hello");
await newPage.getByTestId(TID.buttonSend).last().click();
const stopButton = newPage.getByRole("button", { name: "Stop" });
await stopButton.waitFor({ state: "visible", timeout: 30000 });
const stopButton = newPage.getByRole("button", { name: TEXTS.stop });
await stopButton.waitFor({ state: "visible", timeout: TIMEOUTS.standard });
await newPage.close();
await page.bringToFront();
await page.waitForTimeout(500);
await page.getByTestId("publish-button").click();
await page.waitForTimeout(500);
await page.getByTestId("publish-switch").click();
await page.waitForTimeout(500);
await page.getByTestId(TID.publishButton).click();
await page.getByTestId(TID.publishSwitch).click();
await expect(page.getByTestId("rf__wrapper")).toBeVisible();
await expect(page.getByTestId("publish-switch")).toBeChecked({
await expect(page.getByTestId(TID.publishSwitch)).toBeChecked({
checked: false,
});
await expect(page.getByTestId("rf__wrapper")).toBeVisible();
// The publish-switch toggle is confirmed in the UI above, but the
// un-publish has to reach the backend before the shareable URL stops
// resolving — give it time to propagate.
await page.waitForTimeout(ANIMATIONS.publishTogglePropagation);
await page.goto(newUrl);
await page.waitForTimeout(2000);
try {
await expect(page.getByTestId("mainpage_title")).toBeVisible({
timeout: 10000,
});
} catch (_error) {
await page.reload();
await expect(page.getByTestId("mainpage_title")).toBeVisible({
timeout: 10000,
});
}
// An un-published playground URL redirects to the projects page:
// PlaygroundPage detects access_type !== "PUBLIC" and navigates to
// "/". The redirect is client-side and the subsequent app bootstrap
// can be slow on CI, so wait for the URL to leave /playground/
// before asserting the projects page rendered.
await page.waitForURL((url) => !url.pathname.startsWith("/playground/"), {
timeout: TIMEOUTS.long,
});
await expect(page.getByTestId(TID.mainpageTitle)).toBeVisible({
timeout: TIMEOUTS.long,
});
},
);

View File

@ -1,6 +1,7 @@
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
test(
"user can open component dropdown menu by right-clicking on nodes",
{ tag: ["@release", "@components", "@dropdown", "@right-click"] },
@ -10,7 +11,9 @@ test(
// Start with a basic template that has multiple components
if (await page.getByTestId("components-btn").isVisible()) {
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
}
await page.getByTestId("template-get-started-card-basic-prompting").click();
@ -29,7 +32,7 @@ test(
});
// Test 1: Right-click on Chat Input component should open dropdown immediately (single click)
const chatInputComponent = page.getByText("Chat Input").first();
const chatInputComponent = page.getByText(TEXTS.componentChatInput).first();
// First, click somewhere else to ensure no component is selected
await page.click("body", { position: { x: 100, y: 100 } });

View File

@ -5,6 +5,7 @@ import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { zoomOut } from "../../utils/zoom-out";
import { TEXTS } from "../../utils/constants/texts";
test(
"user should be able to use Run Flow without any issues",
{ tag: ["@release", "@workspace", "@api"] },
@ -22,7 +23,7 @@ test(
await page.getByTestId("blank-flow").click();
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat output");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchChatOutput);
await page.waitForSelector('[data-testid="input_outputChat Output"]', {
timeout: 100000,
});
@ -37,7 +38,7 @@ test(
await zoomOut(page, 2);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat input");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchChatInput);
await page.waitForSelector('[data-testid="input_outputChat Input"]', {
timeout: 100000,
});
@ -49,7 +50,7 @@ test(
});
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("text output");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchTextOutput);
await page.waitForSelector('[data-testid="input_outputText Output"]', {
timeout: 100000,
});
@ -77,7 +78,7 @@ test(
await page.getByTestId("icon-ChevronLeft").click();
await page.getByText("New Flow").isVisible();
await expect(page.getByText("New Flow")).toBeVisible();
await page.getByTestId("new-project-btn").click();
await page.getByTestId("blank-flow").click();
@ -116,7 +117,7 @@ test(
.fill("THIS IS A TEST FOR RUN FLOW COMPONENT");
await page.getByTestId("button_run_run flow").click();
await page.waitForSelector("text=built successfully", {
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 30000,
});
@ -127,7 +128,9 @@ test(
await page.locator('[data-testid^="output-inspection-"]').first().click();
const value = await page.getByPlaceholder("Empty").inputValue();
const value = await page
.getByPlaceholder(TEXTS.placeholderEmpty)
.inputValue();
expect(value).toBe("THIS IS A TEST FOR RUN FLOW COMPONENT");
},

View File

@ -1,115 +0,0 @@
import { readFileSync } from "fs";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { zoomOut } from "../../utils/zoom-out";
test.describe("save component tests", () => {
/// <reference lib="dom"/>
test.skip(
"save group component tests",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
// Read your file into a buffer.
const jsonContent = readFileSync(
"tests/assets/flow_group_test.json",
"utf-8",
);
// Create the DataTransfer and File
const dataTransfer = await page.evaluateHandle((data) => {
const dt = new DataTransfer();
// Convert the buffer to a hex array
const file = new File([data], "flow_group_test.json", {
type: "application/json",
});
dt.items.add(file);
return dt;
}, jsonContent);
// Now dispatch
await page.dispatchEvent(
"//*[@data-testid='rf__wrapper']/div[1]/div",
"drop",
{
dataTransfer,
},
);
const genericNoda = page.getByTestId("div-generic-node");
const elementCount = await genericNoda?.count();
if (elementCount > 0) {
expect(true).toBeTruthy();
}
await adjustScreenView(page, { numberOfZoomOut: 2 });
await page.getByTestId("title-Agent Initializer").click({
modifiers: ["Control"],
});
await page.getByRole("button", { name: "Group" }).click();
await page
.locator('//*[@id="react-flow-id"]')
.first()
.click({ button: "left" });
let textArea = page.getByTestId("div-textarea-description");
let elementCountText = await textArea?.count();
if (elementCountText > 0) {
expect(true).toBeTruthy();
}
let groupNode = page.getByTestId("title-Group");
let elementGroup = await groupNode?.count();
if (elementGroup > 0) {
expect(true).toBeTruthy();
}
await page.getByTestId("title-Group").click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("icon-SaveAll").click();
// timeout to handle case where there is already a saved component with the same name
await page.waitForTimeout(1000);
const replaceButton = await page
.getByTestId("replace-button")
.isVisible();
if (replaceButton) {
await page.getByTestId("replace-button").click();
}
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("group");
await page
.getByText("Group")
.first()
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
await page.getByTestId("fit_view").click();
textArea = page.getByTestId("div-textarea-description");
elementCountText = await textArea?.count();
if (elementCountText > 0) {
expect(true).toBeTruthy();
}
groupNode = page.getByTestId("title-Group");
elementGroup = await groupNode?.count();
if (elementGroup > 0) {
expect(true).toBeTruthy();
}
},
);
});

View File

@ -1,84 +1,29 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
/**
* Helper: create Basic Prompting flow, configure GPT, publish,
* and open the shareable playground in a new tab.
*/
async function setupShareablePlayground(page: any, context: any): Promise<any> {
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await initialGPTsetup(page);
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.getByTestId("publish-button").click();
await page.waitForSelector('[data-testid="shareable-playground"]', {
timeout: 10000,
});
await page.waitForTimeout(1000);
await page.getByTestId("publish-switch").click();
await page.waitForTimeout(2000);
const pagePromise = context.waitForEvent("page");
await page.getByTestId("shareable-playground").click();
const newPage = await pagePromise;
await newPage.waitForTimeout(3000);
return newPage;
}
/**
* Helper: send message and wait for build to complete.
* Uses the same pattern as publish-flow.spec.ts (Stop button lifecycle).
*/
async function sendAndWaitForResponse(playgroundPage: any, message: string) {
// Wait for the chat input to be present. The default actionTimeout (20s)
// was not enough on Windows CI for the shareable-playground page to mount
// the message input.
await playgroundPage
.getByPlaceholder("Send a message...")
.waitFor({ state: "visible", timeout: 60000 });
await playgroundPage.getByPlaceholder("Send a message...").fill(message);
await playgroundPage.getByTestId("button-send").last().click();
// Wait for Stop button to appear (build started)
const stopButton = playgroundPage.getByRole("button", { name: "Stop" });
await stopButton.waitFor({ state: "visible", timeout: 30000 });
// Wait for Stop button to disappear (build complete)
await stopButton.waitFor({ state: "hidden", timeout: 120000 });
}
import { TID } from "../../utils/constants/testIds";
import { TIMEOUTS } from "../../utils/constants/timeouts";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { publishBasicPromptingAndOpenShareablePlayground } from "../../utils/playground/publish-and-open-shareable";
import { sendPlaygroundMessage } from "../../utils/playground/send-playground-message";
test(
"shareable playground: auto-login user can send message and get response",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page, context }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
const { playgroundPage } =
await publishBasicPromptingAndOpenShareablePlayground(page, context);
const playgroundPage = await setupShareablePlayground(page, context);
await sendAndWaitForResponse(playgroundPage, "Say hello");
await sendPlaygroundMessage(playgroundPage, "Say hello", {
surface: "shareable",
});
// After build complete, at least one chat message should be visible
await expect(
playgroundPage.locator('[data-testid="div-chat-message"]').first(),
).toBeVisible({ timeout: 10000 });
playgroundPage.locator(`[data-testid="${TID.chatMessage}"]`).first(),
).toBeVisible({ timeout: TIMEOUTS.medium });
await playgroundPage.close();
},
@ -88,40 +33,19 @@ test(
"shareable playground: streaming works",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page, context }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
const { playgroundPage } =
await publishBasicPromptingAndOpenShareablePlayground(page, context);
const playgroundPage = await setupShareablePlayground(page, context);
// Wait for the chat input to be present (Windows CI is slow to mount it).
await playgroundPage
.getByPlaceholder("Send a message...")
.waitFor({ state: "visible", timeout: 60000 });
await playgroundPage
.getByPlaceholder("Send a message...")
.fill("Tell me a short joke");
await playgroundPage.getByTestId("button-send").last().click();
// Stop button should appear during build (streaming active)
await playgroundPage.waitForSelector('[data-testid="button-stop"]', {
timeout: 30000,
await sendPlaygroundMessage(playgroundPage, "Tell me a short joke", {
surface: "shareable",
});
// Wait for stop button to disappear (streaming finished)
await playgroundPage.waitForSelector('[data-testid="button-stop"]', {
state: "detached",
timeout: 120000,
});
// Send button should be back
// After stop button disappears the send button should be back
await expect(
playgroundPage.getByTestId("button-send").last(),
playgroundPage.getByTestId(TID.buttonSend).last(),
).toBeVisible();
await playgroundPage.close();
@ -132,31 +56,26 @@ test(
"shareable playground: session management works",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page, context }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
const { playgroundPage } =
await publishBasicPromptingAndOpenShareablePlayground(page, context);
const playgroundPage = await setupShareablePlayground(page, context);
await sendAndWaitForResponse(playgroundPage, "Session test");
await sendPlaygroundMessage(playgroundPage, "Session test", {
surface: "shareable",
});
// Create new session
await playgroundPage.getByTestId("new-chat").click();
await playgroundPage.waitForTimeout(2000);
await playgroundPage.getByTestId(TID.newChat).click();
// New session should be created — at least "Default Session" + new one
// Check that the new-chat button is still clickable (UI didn't crash)
await expect(playgroundPage.getByTestId("new-chat")).toBeVisible();
// New session should be created and the UI should not crash
await expect(playgroundPage.getByTestId(TID.newChat)).toBeVisible();
// Verify at least one session-selector exists
await expect(
playgroundPage.getByTestId("session-selector").first(),
).toBeVisible({ timeout: 5000 });
playgroundPage.getByTestId(TID.sessionSelector).first(),
).toBeVisible({ timeout: TIMEOUTS.medium });
await playgroundPage.close();
},

View File

@ -1,161 +1,86 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
// These tests require the server to run with AUTO_LOGIN=FALSE.
// When the server runs with AUTO_LOGIN=TRUE (default for local dev),
// the backend uses client_id instead of user_id for session isolation,
// and persistence features are not active.
// Set LANGFLOW_AUTO_LOGIN=false in your .env to run these tests.
/**
* Helper: mock auto-login as disabled and log in manually.
*/
async function setupAutoLoginOff(page: any) {
await page.route("**/api/v1/auto_login", (route: any) => {
route.fulfill({
status: 500,
contentType: "application/json",
body: JSON.stringify({ detail: { auto_login: false } }),
});
});
import type { Page } from "@playwright/test";
import { expect, test } from "../../fixtures";
import { mockAutoLoginDisabled } from "../../utils/auth/mock-auto-login-disabled";
import { TID } from "../../utils/constants/testIds";
import { TIMEOUTS } from "../../utils/constants/timeouts";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { publishBasicPromptingAndOpenShareablePlayground } from "../../utils/playground/publish-and-open-shareable";
import { sendPlaygroundMessage } from "../../utils/playground/send-playground-message";
import { sessionMoreMenu } from "../../utils/playground/sessions";
await page.addInitScript(() => {
window.process = window.process || ({} as any);
const newEnv = {
...(window.process as any).env,
LANGFLOW_AUTO_LOGIN: "false",
};
Object.defineProperty(window.process, "env", {
value: newEnv,
writable: true,
configurable: true,
});
sessionStorage.setItem("testMockAutoLogin", "true");
});
import { TEXTS } from "../../utils/constants/texts";
/**
* Stub auto-login, navigate to the sign-in page, log in manually.
*/
async function setupAutoLoginOff(page: Page): Promise<void> {
await mockAutoLoginDisabled(page);
await page.goto("/");
await page.waitForSelector("text=sign in to langflow", { timeout: 30000 });
await page.waitForSelector(`text=${TEXTS.authSignInHeader}`, {
timeout: TIMEOUTS.standard,
});
await page.getByPlaceholder("Username").fill("langflow");
await page.getByPlaceholder("Password").fill("langflow");
await page
.getByPlaceholder(TEXTS.placeholderUsername)
.fill(TEXTS.authDefaultCredential);
await page
.getByPlaceholder(TEXTS.placeholderPassword)
.fill(TEXTS.authDefaultCredential);
await page.evaluate(() => {
sessionStorage.removeItem("testMockAutoLogin");
});
await page.getByRole("button", { name: "Sign In" }).click();
await page.getByRole("button", { name: TEXTS.signIn }).click();
await page.waitForSelector('[data-testid="mainpage_title"]', {
timeout: 30000,
await page.waitForSelector(`[data-testid="${TID.mainpageTitle}"]`, {
timeout: TIMEOUTS.standard,
});
}
/**
* Helper: create Basic Prompting flow, configure GPT, publish,
* and return the shareable playground URL.
*/
async function createPublishAndGetUrl(
page: any,
context: any,
): Promise<string> {
await page.waitForSelector('[id="new-project-btn"]', { timeout: 30000 });
await awaitBootstrapTest(page, { skipGoto: true });
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await initialGPTsetup(page);
// Build first
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
// Publish
await page.getByTestId("publish-button").click();
await page.waitForSelector('[data-testid="shareable-playground"]', {
timeout: 10000,
});
await page.waitForTimeout(1000);
await page.getByTestId("publish-switch").click();
await page.waitForTimeout(2000);
// Get URL
const pagePromise = context.waitForEvent("page");
await page.getByTestId("shareable-playground").click();
const newPage = await pagePromise;
await newPage.waitForTimeout(2000);
const url = newPage.url();
await newPage.close();
return url;
}
/**
* Helper: send a message and wait for the build to complete.
*/
async function sendMessageAndWait(page: any, message: string) {
await page.getByPlaceholder("Send a message...").fill(message);
await page.getByTestId("button-send").last().click();
// Wait for Stop button lifecycle (build started → completed)
const stopButton = page.getByRole("button", { name: "Stop" });
await stopButton.waitFor({ state: "visible", timeout: 30000 });
await stopButton.waitFor({ state: "hidden", timeout: 120000 });
await page.waitForTimeout(2000);
}
test(
"shareable playground: logged-in user messages persist after page refresh",
{ tag: ["@release", "@api", "@database"] },
async ({ page, context }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
test.skip(
process?.env?.LANGFLOW_AUTO_LOGIN !== "false",
"Server must run with AUTO_LOGIN=FALSE for persistence tests",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
skipIfMissing.autoLoginDisabled();
loadDotenvIfLocal(__dirname);
await setupAutoLoginOff(page);
const playgroundUrl = await createPublishAndGetUrl(page, context);
const { url: playgroundUrl, playgroundPage } =
await publishBasicPromptingAndOpenShareablePlayground(page, context, {
skipBootstrap: true,
});
await playgroundPage.close();
// Navigate to shareable playground
await page.goto(playgroundUrl);
await page.waitForSelector('[data-testid="button-send"]', {
timeout: 30000,
await page.waitForSelector(`[data-testid="${TID.buttonSend}"]`, {
timeout: TIMEOUTS.standard,
});
// Send message
await sendMessageAndWait(page, "persist test");
await sendPlaygroundMessage(page, "persist test", {
surface: "shareable",
});
// Should have messages
const messagesBefore = await page
.locator('[data-testid="div-chat-message"]')
.locator(`[data-testid="${TID.chatMessage}"]`)
.count();
expect(messagesBefore).toBeGreaterThanOrEqual(2);
// Refresh
await page.reload();
await page.waitForSelector('[data-testid="button-send"]', {
timeout: 30000,
await page.waitForSelector(`[data-testid="${TID.buttonSend}"]`, {
timeout: TIMEOUTS.standard,
});
await page.waitForTimeout(3000);
// Messages should still be visible
const messagesAfter = await page
.locator('[data-testid="div-chat-message"]')
.locator(`[data-testid="${TID.chatMessage}"]`)
.count();
expect(messagesAfter).toBeGreaterThanOrEqual(2);
},
@ -165,37 +90,31 @@ test(
"shareable playground: default session appears first",
{ tag: ["@release", "@api", "@database"] },
async ({ page, context }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
test.skip(
process?.env?.LANGFLOW_AUTO_LOGIN !== "false",
"Server must run with AUTO_LOGIN=FALSE for persistence tests",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
skipIfMissing.autoLoginDisabled();
loadDotenvIfLocal(__dirname);
await setupAutoLoginOff(page);
const playgroundUrl = await createPublishAndGetUrl(page, context);
const { url: playgroundUrl, playgroundPage } =
await publishBasicPromptingAndOpenShareablePlayground(page, context, {
skipBootstrap: true,
});
await playgroundPage.close();
await page.goto(playgroundUrl);
await page.waitForSelector('[data-testid="button-send"]', {
timeout: 30000,
await page.waitForSelector(`[data-testid="${TID.buttonSend}"]`, {
timeout: TIMEOUTS.standard,
});
// Send message in default session
await sendMessageAndWait(page, "default session msg");
await sendPlaygroundMessage(page, "default session msg", {
surface: "shareable",
});
// Create new session
await page.getByTestId("new-chat").click();
await page.waitForTimeout(1000);
await page.getByTestId(TID.newChat).click();
// First session should be Default Session
const firstSession = page.getByTestId("session-selector").first();
const firstSession = page.getByTestId(TID.sessionSelector).first();
await expect(firstSession).toContainText("Default Session");
},
);
@ -204,60 +123,47 @@ test(
"shareable playground: delete session persists after refresh",
{ tag: ["@release", "@api", "@database"] },
async ({ page, context }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
test.skip(
process?.env?.LANGFLOW_AUTO_LOGIN !== "false",
"Server must run with AUTO_LOGIN=FALSE for persistence tests",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
skipIfMissing.autoLoginDisabled();
loadDotenvIfLocal(__dirname);
await setupAutoLoginOff(page);
const playgroundUrl = await createPublishAndGetUrl(page, context);
const { url: playgroundUrl, playgroundPage } =
await publishBasicPromptingAndOpenShareablePlayground(page, context, {
skipBootstrap: true,
});
await playgroundPage.close();
await page.goto(playgroundUrl);
await page.waitForSelector('[data-testid="button-send"]', {
timeout: 30000,
await page.waitForSelector(`[data-testid="${TID.buttonSend}"]`, {
timeout: TIMEOUTS.standard,
});
// Send message
await sendMessageAndWait(page, "keep this");
await sendPlaygroundMessage(page, "keep this", { surface: "shareable" });
// Create new session and send message
await page.getByTestId("new-chat").click();
await page.waitForTimeout(1000);
await sendMessageAndWait(page, "delete this");
// Create new session and send a message in it
await page.getByTestId(TID.newChat).click();
await sendPlaygroundMessage(page, "delete this", { surface: "shareable" });
const sessionsBefore = await page.getByTestId("session-selector").count();
const sessionsBefore = await page.getByTestId(TID.sessionSelector).count();
// Delete last session
await page
.locator('[data-testid^="session-"][data-testid$="-more-menu"]')
.last()
.click();
await sessionMoreMenu(page, "last").click();
await page.getByTestId("delete-session-option").click();
await page.waitForTimeout(2000);
const sessionsAfterDelete = await page
.getByTestId("session-selector")
.getByTestId(TID.sessionSelector)
.count();
expect(sessionsAfterDelete).toBeLessThan(sessionsBefore);
// Refresh
await page.reload();
await page.waitForSelector('[data-testid="button-send"]', {
timeout: 30000,
await page.waitForSelector(`[data-testid="${TID.buttonSend}"]`, {
timeout: TIMEOUTS.standard,
});
await page.waitForTimeout(3000);
const sessionsAfterRefresh = await page
.getByTestId("session-selector")
.getByTestId(TID.sessionSelector)
.count();
expect(sessionsAfterRefresh).toBeLessThanOrEqual(sessionsAfterDelete);
},
@ -267,57 +173,45 @@ test(
"shareable playground: rename session persists after refresh",
{ tag: ["@release", "@api", "@database"] },
async ({ page, context }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
test.skip(
process?.env?.LANGFLOW_AUTO_LOGIN !== "false",
"Server must run with AUTO_LOGIN=FALSE for persistence tests",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
skipIfMissing.autoLoginDisabled();
loadDotenvIfLocal(__dirname);
await setupAutoLoginOff(page);
const playgroundUrl = await createPublishAndGetUrl(page, context);
const { url: playgroundUrl, playgroundPage } =
await publishBasicPromptingAndOpenShareablePlayground(page, context, {
skipBootstrap: true,
});
await playgroundPage.close();
await page.goto(playgroundUrl);
await page.waitForSelector('[data-testid="button-send"]', {
timeout: 30000,
await page.waitForSelector(`[data-testid="${TID.buttonSend}"]`, {
timeout: TIMEOUTS.standard,
});
// Create new session and send message
await page.getByTestId("new-chat").click();
await page.waitForTimeout(1000);
await sendMessageAndWait(page, "rename test");
await page.getByTestId(TID.newChat).click();
await sendPlaygroundMessage(page, "rename test", { surface: "shareable" });
// Rename session
await page
.locator('[data-testid^="session-"][data-testid$="-more-menu"]')
.last()
.click();
await sessionMoreMenu(page, "last").click();
await page.getByTestId("rename-session-option").click();
await page.getByTestId("session-rename-input").fill("Custom Name");
await page.keyboard.press("Enter");
await page.waitForTimeout(1000);
await expect(
page.getByTestId("session-selector").getByText("Custom Name"),
).toBeVisible({ timeout: 5000 });
page.getByTestId(TID.sessionSelector).getByText("Custom Name"),
).toBeVisible({ timeout: TIMEOUTS.medium });
// Refresh
await page.reload();
await page.waitForSelector('[data-testid="button-send"]', {
timeout: 30000,
await page.waitForSelector(`[data-testid="${TID.buttonSend}"]`, {
timeout: TIMEOUTS.standard,
});
await page.waitForTimeout(3000);
// Renamed session should persist
await expect(
page.getByTestId("session-selector").getByText("Custom Name"),
).toBeVisible({ timeout: 10000 });
page.getByTestId(TID.sessionSelector).getByText("Custom Name"),
).toBeVisible({ timeout: TIMEOUTS.medium });
},
);

View File

@ -1,70 +1,39 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TID } from "../../utils/constants/testIds";
import { TIMEOUTS } from "../../utils/constants/timeouts";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { buildFlowAndWait } from "../../utils/flow/build-flow-and-wait";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { publishBasicPromptingAndOpenShareablePlayground } from "../../utils/playground/publish-and-open-shareable";
import { sendPlaygroundMessage } from "../../utils/playground/send-playground-message";
import { TEXTS } from "../../utils/constants/texts";
test(
"shareable playground: bot messages display token usage",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page, context }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
const { playgroundPage } =
await publishBasicPromptingAndOpenShareablePlayground(page, context);
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await initialGPTsetup(page);
// Build first
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
// Publish
await page.getByTestId("publish-button").click();
await page.waitForSelector('[data-testid="shareable-playground"]', {
timeout: 10000,
await sendPlaygroundMessage(playgroundPage, "Say hi", {
surface: "shareable",
});
await page.waitForTimeout(1000);
await page.getByTestId("publish-switch").click();
await page.waitForTimeout(2000);
const pagePromise = context.waitForEvent("page");
await page.getByTestId("shareable-playground").click();
const newPage = await pagePromise;
await newPage.waitForTimeout(3000);
// Send message
await newPage.getByPlaceholder("Send a message...").fill("Say hi");
await newPage.getByTestId("button-send").last().click();
// Wait for build to complete (Stop button lifecycle)
const stopButton = newPage.getByRole("button", { name: "Stop" });
await stopButton.waitFor({ state: "visible", timeout: 30000 });
await stopButton.waitFor({ state: "hidden", timeout: 120000 });
// Wait for UI to settle
await newPage.waitForTimeout(3000);
// Token count should be visible (Coins icon indicates token display)
await newPage.waitForSelector('[data-testid="icon-Coins"]', {
timeout: 30000,
await playgroundPage.waitForSelector(`[data-testid="${TID.iconCoins}"]`, {
timeout: TIMEOUTS.standard,
});
const coinsIcons = await newPage
.locator('[data-testid="icon-Coins"]')
const coinsIcons = await playgroundPage
.locator(`[data-testid="${TID.iconCoins}"]`)
.count();
expect(coinsIcons).toBeGreaterThan(0);
await newPage.close();
await playgroundPage.close();
},
);
@ -72,48 +41,34 @@ test(
"regular playground: Finished In with token display still works (regression)",
{ tag: ["@release", "@workspace", "@api"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await openStarterProject(page, "Basic Prompting");
await initialGPTsetup(page);
// Build
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await buildFlowAndWait(page);
// Open regular playground
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page.waitForSelector('[data-testid="input-chat-playground"]', {
timeout: 10000,
await page
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForSelector(`[data-testid="${TID.inputChatPlayground}"]`, {
timeout: TIMEOUTS.medium,
});
// Send message
await page.getByTestId("input-chat-playground").click();
await page.getByTestId("input-chat-playground").fill("Say hello briefly");
await page.getByTestId(TID.inputChatPlayground).click();
await page.getByTestId(TID.inputChatPlayground).fill("Say hello briefly");
await page.keyboard.press("Enter");
// Wait for AI response
await page.waitForFunction(
() =>
document.querySelectorAll('[data-testid="div-chat-message"]').length >=
2,
{ timeout: 120000 },
{ timeout: TIMEOUTS.buildComplete },
);
// "Finished in" should appear (regular playground specific)
await expect(page.getByText("Finished in")).toBeVisible({
timeout: 30000,
timeout: TIMEOUTS.standard,
});
},
);

View File

@ -7,6 +7,7 @@ import { removeOldApiKeys } from "../../utils/remove-old-api-keys";
import { updateOldComponents } from "../../utils/update-old-components";
import { zoomOut } from "../../utils/zoom-out";
import { TEXTS } from "../../utils/constants/texts";
// TODO: fix this test
test(
"user must be able to stop a building",
@ -20,7 +21,7 @@ test(
//first component
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("text input");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchTextInput);
await page
.getByTestId("input_outputText Input")
@ -32,7 +33,7 @@ test(
//second component
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("url");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchUrl);
await page
.getByTestId("data_sourceURL")
@ -65,7 +66,7 @@ test(
//fifth component
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat output");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchChatOutput);
await page
.getByTestId("input_outputChat Output")

View File

@ -1,9 +1,10 @@
import * as dotenv from "dotenv";
import path from "path";
import { test } from "../../fixtures";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { renameFlow } from "../../utils/rename-flow";
import { TEXTS } from "../../utils/constants/texts";
test(
"should filter by tag",
{ tag: ["@release", "@api"] },
@ -28,14 +29,13 @@ test(
});
await page
.getByPlaceholder("Insert your API Key")
.getByPlaceholder(TEXTS.placeholderApiKey)
.fill(process.env.STORE_API_KEY ?? "");
await page.getByTestId("api-key-save-button-store").click();
await page.waitForTimeout(1000);
await page.getByText("Success! Your API Key has been saved.").isVisible();
await expect(page.getByText(TEXTS.toastApiKeySaved)).toBeVisible();
await page.getByTestId("button-store").click();
await page.waitForTimeout(1000);
@ -66,7 +66,7 @@ test(
await safeClick("tag-selector-Vector Store");
await safeClick("tag-selector-Memory");
await page.getByText("Basic RAG").isVisible();
await expect(page.getByText(TEXTS.templateBasicRag)).toBeVisible();
},
);
@ -91,14 +91,13 @@ test("should share component with share button", async ({ page }) => {
});
await page
.getByPlaceholder("Insert your API Key")
.getByPlaceholder(TEXTS.placeholderApiKey)
.fill(process.env.STORE_API_KEY ?? "");
await page.getByTestId("api-key-save-button-store").click();
await page.waitForTimeout(1000);
await page.getByText("Success! Your API Key has been saved.").isVisible();
await expect(page.getByText(TEXTS.toastApiKeySaved)).toBeVisible();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 100000,
});
@ -114,7 +113,9 @@ test("should share component with share button", async ({ page }) => {
const randomName = Math.random().toString(36).substring(2);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await renameFlow(page, { flowName: randomName });
@ -123,33 +124,32 @@ test("should share component with share button", async ({ page }) => {
});
await page.getByTestId("shared-button-flow").first().click();
await page.getByText("Name:").isVisible();
await page.getByText("Description:").isVisible();
await page.getByText("Set workflow status to public").isVisible();
await expect(page.getByText("Name:")).toBeVisible();
await expect(page.getByText("Description:")).toBeVisible();
await expect(page.getByText("Set workflow status to public")).toBeVisible();
await page
.getByText(
"Attention: API keys in specified fields are automatically removed upon sharing.",
)
.isVisible();
await page.getByText("Export").first().isVisible();
await page.getByText("Share Flow").first().isVisible();
await expect(page.getByText("Export").first()).toBeVisible();
await expect(page.getByText("Share Flow").first()).toBeVisible();
await page.waitForTimeout(3000);
await page.getByText("Agent").first().isVisible();
await page.getByText("Memory").first().isVisible();
await page.getByText("Chain").first().isVisible();
await page.getByText("Vector Store").first().isVisible();
await page.getByText("Prompt").last().isVisible();
await expect(page.getByText("Agent").first()).toBeVisible();
await expect(page.getByText("Memory").first()).toBeVisible();
await expect(page.getByText("Chain").first()).toBeVisible();
await expect(page.getByText("Vector Store").first()).toBeVisible();
await expect(page.getByText("Prompt").last()).toBeVisible();
await page.getByTestId("public-checkbox").isChecked();
const flowName = await page.getByTestId("input-flow-name").inputValue();
const flowDescription = await page
.getByPlaceholder("Flow description")
.inputValue();
await page.getByText(flowName).last().isVisible();
await page.getByText(flowDescription).last().isVisible();
await expect(page.getByText(flowName).last()).toBeVisible();
await expect(page.getByText(flowDescription).last()).toBeVisible();
await page.waitForTimeout(1000);
await page.getByText("Flow shared successfully").last().isVisible();
await expect(page.getByText("Flow shared successfully").last()).toBeVisible();
});

View File

@ -6,6 +6,7 @@ import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { openAdvancedOptions } from "../../utils/open-advanced-options";
import { TEXTS } from "../../utils/constants/texts";
/**
* E2E coverage for the Native Structured Output feature on the Agent
* component (CZL/MANUAL_TEST_NATIVE_STRUCTURED_OUTPUT.md). The tests
@ -40,7 +41,10 @@ test(
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Simple Agent" }).first().click();
await page
.getByRole("heading", { name: TEXTS.templateSimpleAgent })
.first()
.click();
await initialGPTsetup(page);
@ -84,7 +88,10 @@ test(
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Simple Agent" }).first().click();
await page
.getByRole("heading", { name: TEXTS.templateSimpleAgent })
.first()
.click();
await initialGPTsetup(page);
@ -126,7 +133,10 @@ test(
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Simple Agent" }).first().click();
await page
.getByRole("heading", { name: TEXTS.templateSimpleAgent })
.first()
.click();
await initialGPTsetup(page);

View File

@ -4,6 +4,7 @@ import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { TEXTS } from "../../utils/constants/texts";
test.describe("Token Usage Tracking", () => {
test(
"node badge should show token count after running an LLM flow",
@ -21,7 +22,9 @@ test.describe("Token Usage Tracking", () => {
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
});
@ -29,7 +32,7 @@ test.describe("Token Usage Tracking", () => {
await initialGPTsetup(page);
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForSelector('[data-testid="input-chat-playground"]', {
@ -73,7 +76,9 @@ test.describe("Token Usage Tracking", () => {
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
});
@ -81,7 +86,7 @@ test.describe("Token Usage Tracking", () => {
await initialGPTsetup(page);
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForSelector('[data-testid="input-chat-playground"]', {

View File

@ -1,32 +0,0 @@
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
test.describe("group node test", () => {
/// <reference lib="dom"/>
// TODO: fix this test
test.skip(
"group and ungroup updating values",
{ tag: ["@release", "@workspace", "@components"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page
.getByRole("heading", { name: "Basic Prompting" })
.first()
.click();
await page.getByTestId("fit_view").first().click();
await page
.getByTestId("title-OpenAI")
.click({ modifiers: ["ControlOrMeta"] });
await page
.getByTestId("title-Prompt Template")
.click({ modifiers: ["ControlOrMeta"] });
await page.getByRole("button", { name: "Group" }).click();
await page.getByTestId("title-Group").click();
await expect(page.getByTestId("tool-mode-button")).toBeHidden();
},
);
});

View File

@ -3,6 +3,7 @@ import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
test(
"should able to see and interact with Traces",
{ tag: ["@release", "@workspace", "@api"] },
@ -31,7 +32,9 @@ test(
response.status() === 201,
{ timeout: 120000 },
);
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await flowCreatePromise;
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
@ -99,7 +102,9 @@ test.skip(
response.status() === 201,
{ timeout: 120000 },
);
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await flowCreatePromise;
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,

View File

@ -2,6 +2,7 @@ import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
test(
"curl_api_generation",
{ tag: ["@release", "@workspace", "@api"] },
@ -10,7 +11,9 @@ test(
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
// Wait for the new-flow Loading state to clear before checking the
// publish button — the canvas mounts only after the flow finishes
// loading, which can outlast a 20s action timeout on Windows CI.
@ -33,7 +36,7 @@ test(
expect(clipboardContent.length).toBeGreaterThan(0);
await page.getByTestId("tweaks-button").click();
await page
.getByRole("heading", { name: "Language Model" })
.getByRole("heading", { name: TEXTS.componentLanguageModel })
.locator("div")
.first()
.click();
@ -44,7 +47,7 @@ test(
await page.getByTestId("showstream").first().click();
await page.getByText("Close").last().click();
await page.getByText(TEXTS.close).last().click();
await page.getByTestId("api_tab_curl").click();
await page.getByTestId("icon-Copy").click();
@ -56,7 +59,7 @@ test(
expect(oldValue).not.toBe(newValue);
expect(clipboardContent2.length).toBeGreaterThan(clipboardContent.length);
await awaitBootstrapTest(page, { skipModal: true });
await page.getByText("Basic Prompting").first().click();
await page.getByText(TEXTS.templateBasicPrompting).first().click();
await page.getByTestId("publish-button").click();
await page.getByTestId("api-access-item").click();
expect(
@ -119,7 +122,7 @@ test("check if tweaks are updating when someothing on the flow changes", async (
await page.getByText("collection_name_test_123123123!@#$&*(&%$@").isVisible();
await page.getByText("persist_directory_123123123!@#$&*(&%$@").isVisible();
await page.getByText("Close").last().click();
await page.getByText(TEXTS.close).last().click();
await page.getByText("Python", { exact: true }).click();

View File

@ -1,6 +1,7 @@
import { expect, test } from "../../fixtures";
import { renameFlow } from "../../utils/rename-flow";
import { TEXTS } from "../../utils/constants/texts";
test(
"flow state should be properly cleaned up between user sessions",
{ tag: ["@release", "@api", "@database"] },
@ -43,9 +44,15 @@ test(
// Log in as admin and create test user
await page.goto("/");
await page.waitForSelector("text=sign in to langflow", { timeout: 30000 });
await page.getByPlaceholder("Username").fill("langflow");
await page.getByPlaceholder("Password").fill("langflow");
await page.waitForSelector(`text=${TEXTS.authSignInHeader}`, {
timeout: 30000,
});
await page
.getByPlaceholder(TEXTS.placeholderUsername)
.fill(TEXTS.authDefaultCredential);
await page
.getByPlaceholder(TEXTS.placeholderPassword)
.fill(TEXTS.authDefaultCredential);
await page.evaluate(() => {
sessionStorage.removeItem("testMockAutoLogin");
});
@ -55,7 +62,7 @@ test(
response.url().includes("/api/v1/login") && response.status() === 200,
{ timeout: 60000 },
),
page.getByRole("button", { name: "Sign In" }).click(),
page.getByRole("button", { name: TEXTS.signIn }).click(),
]);
// mainpage_title only renders after the homepage data finishes loading,
@ -66,13 +73,16 @@ test(
await page.getByTestId("user-profile-settings").click();
await page.getByText("Admin Page", { exact: true }).click();
await page.getByText("New User", { exact: true }).click();
await page.getByPlaceholder("Username").last().fill(userAName);
await page
.getByPlaceholder(TEXTS.placeholderUsername)
.last()
.fill(userAName);
await page.locator('input[name="password"]').fill(userAPassword);
await page.locator('input[name="confirmpassword"]').fill(userAPassword);
await page.waitForSelector("#is_active", { timeout: 1500 });
await page.locator("#is_active").click();
await expect(page.locator("#is_active")).toBeChecked();
await page.getByText("Save", { exact: true }).click();
await page.getByText(TEXTS.save, { exact: true }).click();
await page.waitForSelector("text=new user added", { timeout: 30000 });
// Log out from admin
@ -84,14 +94,16 @@ test(
await page.evaluate(() => {
sessionStorage.setItem("testMockAutoLogin", "true");
});
await page.getByText("Logout", { exact: true }).click();
await page.getByText(TEXTS.logout, { exact: true }).click();
// ---- USER A SESSION ----
// Log in as User A
await page.waitForSelector("text=sign in to langflow", { timeout: 30000 });
await page.getByPlaceholder("Username").fill(userAName);
await page.getByPlaceholder("Password").fill(userAPassword);
await page.waitForSelector(`text=${TEXTS.authSignInHeader}`, {
timeout: 30000,
});
await page.getByPlaceholder(TEXTS.placeholderUsername).fill(userAName);
await page.getByPlaceholder(TEXTS.placeholderPassword).fill(userAPassword);
await page.evaluate(() => {
sessionStorage.removeItem("testMockAutoLogin");
});
@ -101,7 +113,7 @@ test(
response.url().includes("/api/v1/login") && response.status() === 200,
{ timeout: 60000 },
),
page.getByRole("button", { name: "Sign In" }).click(),
page.getByRole("button", { name: TEXTS.signIn }).click(),
]);
// Create a flow for User A
@ -159,14 +171,20 @@ test(
await page.evaluate(() => {
sessionStorage.setItem("testMockAutoLogin", "true");
});
await page.getByText("Logout", { exact: true }).click();
await page.getByText(TEXTS.logout, { exact: true }).click();
// ---- ADMIN SESSION AGAIN ----
// Log in as admin again
await page.waitForSelector("text=sign in to langflow", { timeout: 30000 });
await page.getByPlaceholder("Username").fill("langflow");
await page.getByPlaceholder("Password").fill("langflow");
await page.waitForSelector(`text=${TEXTS.authSignInHeader}`, {
timeout: 30000,
});
await page
.getByPlaceholder(TEXTS.placeholderUsername)
.fill(TEXTS.authDefaultCredential);
await page
.getByPlaceholder(TEXTS.placeholderPassword)
.fill(TEXTS.authDefaultCredential);
await page.evaluate(() => {
sessionStorage.removeItem("testMockAutoLogin");
});
@ -176,7 +194,7 @@ test(
response.url().includes("/api/v1/login") && response.status() === 200,
{ timeout: 60000 },
),
page.getByRole("button", { name: "Sign In" }).click(),
page.getByRole("button", { name: TEXTS.signIn }).click(),
]);
// Verify admin can't see User A's flow

View File

@ -5,6 +5,7 @@ import { addNewUserAndLogin } from "../../utils/add-new-user-and-loggin";
import { cleanAllFlows } from "../../utils/clean-all-flows";
import { cleanOldFolders } from "../../utils/clean-old-folders";
import { TEXTS } from "../../utils/constants/texts";
test(
"admin user must be able to track their progress in getting started",
{ tag: ["@release", "@api"] },
@ -81,7 +82,9 @@ async function progressTrackTestFn(
await page.getByTestId("new_project_btn_empty_page").click();
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 100000,

View File

@ -1,6 +1,7 @@
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
// TODO: Need to review the voice assistant vs text to voice
test.skip(
"should able to see and interact with voice assistant",
@ -29,7 +30,9 @@ test.skip(
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.getByTestId("playground-btn-flow-io").click();
await expect(page.getByTestId("voice-button")).toBeVisible();
@ -95,7 +98,9 @@ test.skip(
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.getByTestId("playground-btn-flow-io").click();
await expect(page.getByTestId("voice-button")).not.toBeVisible();
@ -123,7 +128,9 @@ test.skip(
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.getByTestId("playground-btn-flow-io").click();
await expect(page.getByTestId("voice-button")).toBeVisible();

View File

@ -1,43 +1,34 @@
import * as dotenv from "dotenv";
import path from "path";
import { test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { expect, test } from "../../fixtures";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { buildFlowAndWait } from "../../utils/flow/build-flow-and-wait";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Basic Prompting (Hello, World)",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await openStarterProject(page, "Basic Prompting");
await initialGPTsetup(page);
await buildFlowAndWait(page);
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByText("No input message provided.", { exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page
.getByText(TEXTS.labelNoInputMessage, { exact: true })
.last()
.isVisible();
//create a new session - default session can not be deleted
await page.getByTestId("new-chat").click();
await page.getByTitle("New Session 0").isVisible();
await expect(page.getByTitle("New Session 0")).toBeVisible();
await page.waitForSelector('[data-testid="input-chat-playground"]', {
timeout: 100000,
});
@ -57,16 +48,35 @@ withEventDeliveryModes(
timeout: 100000,
});
await page.getByText("matey").last().isVisible();
await expect(page.getByText("matey").last()).toBeVisible();
await page.getByText("timestamp", { exact: true }).last().isVisible();
await page.getByText("text", { exact: true }).last().isVisible();
await page.getByText("sender", { exact: true }).last().isVisible();
await page.getByText("sender_name", { exact: true }).last().isVisible();
await page.getByText("session_id", { exact: true }).last().isVisible();
await page.getByText("files", { exact: true }).last().isVisible();
// Open the message logs table view to verify metadata columns
// (timestamp/text/sender/...). The header chat-menu is hidden in
// fullscreen, so click the sidebar session more-menu instead.
await page
.locator('[data-testid^="session-"][data-testid$="-more-menu"]')
.last()
.click();
await page.getByTestId("message-logs-option").click();
await page.getByRole("gridcell").last().isVisible();
await expect(
page.getByText("timestamp", { exact: true }).last(),
).toBeVisible();
await expect(page.getByText("text", { exact: true }).last()).toBeVisible();
await expect(
page.getByText("sender", { exact: true }).last(),
).toBeVisible();
await expect(
page.getByText("sender_name", { exact: true }).last(),
).toBeVisible();
await expect(
page.getByText("session_id", { exact: true }).last(),
).toBeVisible();
await expect(page.getByText("files", { exact: true }).last()).toBeVisible();
await expect(page.getByRole("gridcell").last()).toBeVisible();
// Close the logs panel so the rest of the playground UI is reachable again.
await page.getByRole("button", { name: TEXTS.close }).click();
// Use sidebar session more menu (chat-header-more-menu is hidden in fullscreen)
await page
.locator('[data-testid^="session-"][data-testid$="-more-menu"]')
@ -77,6 +87,8 @@ withEventDeliveryModes(
timeout: 100000,
});
await page.getByTestId("input-chat-playground").last().isVisible();
await expect(
page.getByTestId("input-chat-playground").last(),
).toBeVisible();
},
);

View File

@ -1,27 +1,18 @@
import * as dotenv from "dotenv";
import path from "path";
import { test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { expect, test } from "../../fixtures";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Blog Writer",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Blog Writer" }).click();
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await openStarterProject(page, "Blog Writer");
await initialGPTsetup(page);
@ -51,11 +42,13 @@ withEventDeliveryModes(
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", {
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 30000 * 3,
});
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page
.getByPlaceholder(
"No chat input variables found. Click to run your flow.",
@ -64,8 +57,8 @@ withEventDeliveryModes(
.last()
.isVisible();
await page.getByText("turtles").last().isVisible();
await page.getByText("sea").last().isVisible();
await page.getByText("survival").last().isVisible();
await expect(page.getByText("turtles").last()).toBeVisible();
await expect(page.getByText("sea").last()).toBeVisible();
await expect(page.getByText("survival").last()).toBeVisible();
},
);

View File

@ -1,11 +1,11 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { getAllResponseMessage } from "../../utils/get-all-response-message";
import { selectAnthropicModel } from "../../utils/select-anthropic-model";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Custom Component Generator",
{ tag: ["@release", "@starter-projects"] },
@ -14,11 +14,7 @@ withEventDeliveryModes(
!process?.env?.ANTHROPIC_API_KEY,
"ANTHROPIC_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
loadDotenvIfLocal(__dirname);
await page.goto("/");
await awaitBootstrapTest(page);
@ -44,7 +40,7 @@ withEventDeliveryModes(
await page.waitForTimeout(1000);
const stopButton = page.getByRole("button", { name: "Stop" });
const stopButton = page.getByRole("button", { name: TEXTS.stop });
await stopButton.waitFor({ state: "hidden", timeout: 30000 * 3 });
const textContents = await getAllResponseMessage(page);

View File

@ -1,28 +1,20 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { uploadFile } from "../../utils/upload-file";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { buildFlowAndWait } from "../../utils/flow/build-flow-and-wait";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Document Q&A",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Document Q&A" }).click();
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await openStarterProject(page, "Document Q&A");
await initialGPTsetup(page);
await uploadFile(page, "test_file.txt");
@ -30,13 +22,13 @@ withEventDeliveryModes(
await page.waitForSelector('[data-testid="button_run_chat output"]', {
timeout: 3000,
});
await buildFlowAndWait(page);
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByText("No input message provided.", { exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page
.getByText(TEXTS.labelNoInputMessage, { exact: true })
.last()
.isVisible();
@ -56,7 +48,7 @@ withEventDeliveryModes(
timeout: 10000,
});
await page.getByText("this is a test file").last().isVisible();
expect(await page.getByTestId("div-chat-message).last().count()).toBe(1)"));
await expect(page.getByText("this is a test file").last()).toBeVisible();
expect(await page.getByTestId("div-chat-message").last().count()).toBe(1);
},
);

View File

@ -1,56 +0,0 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
test.skip(
"Dynamic Agent",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
test.skip(
!process?.env?.SEARCH_API_KEY,
"SEARCH_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Dynamic Agent" }).last().click();
await initialGPTsetup(page);
await page
.getByTestId("popover-anchor-input-api_key")
.last()
.fill(process.env.SEARCH_API_KEY ?? "");
await page
.getByTestId("textarea_str_input_value")
.first()
.fill("how much is an apple stock today");
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", {
timeout: 60000 * 3,
});
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page.waitForTimeout(1000);
expect(page.getByText("apple").last()).toBeVisible();
const textContents = await page
.getByTestId("div-chat-message")
.allTextContents();
const concatAllText = textContents.join(" ");
expect(concatAllText.toLocaleLowerCase()).toContain("apple");
expect(concatAllText.toLocaleLowerCase()).not.toContain("error");
expect(concatAllText.toLocaleLowerCase()).not.toContain("apologize");
const allTextLength = concatAllText.length;
expect(allTextLength).toBeGreaterThan(100);
},
);

View File

@ -1,74 +0,0 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
withEventDeliveryModes(
"Financial Report Parser",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await page.goto("/");
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page
.getByRole("heading", { name: "Financial Report Parser" })
.click();
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
});
await initialGPTsetup(page);
await page.getByText("Parser", { exact: true }).last().click();
await page.getByTestId("tab_1_stringify").click();
await page.getByTestId("playground-btn-flow-io").click();
await page.waitForSelector('[data-testid="button-send"]', {
timeout: 3000,
});
await page.getByTestId("button-send").click();
try {
// Wait for the flow building indicator to appear and then disappear
await page.waitForSelector('[data-testid="stop_building_button"]', {
timeout: 30000,
state: "visible",
});
await page.waitForSelector('[data-testid="stop_building_button"]', {
timeout: 180000,
state: "hidden",
});
} catch (_error) {
console.error("Timeout error");
test.skip(true, "Timeout error");
}
// Wait for the chat response to appear
await page.waitForSelector('[data-testid="div-chat-message"]', {
timeout: 30000,
});
const textContents = await page
.getByTestId("div-chat-message")
.last()
.allTextContents();
const concatAllText = textContents.join(" ").toLowerCase();
expect(concatAllText.length).toBeGreaterThan(10);
expect(concatAllText).toContain("ebitda");
},
);

View File

@ -1,53 +0,0 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
test.skip(
"Hierarchical Tasks Agent",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
test.skip(
!process?.env?.SEARCH_API_KEY,
"SEARCH_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page
.getByRole("heading", { name: "Hierarchical Tasks Agent" })
.first()
.click();
await initialGPTsetup(page);
await page
.getByTestId("popover-anchor-input-api_key")
.last()
.fill(process.env.SEARCH_API_KEY ?? "");
await page.waitForTimeout(1000);
await page.getByTestId("button_run_chat output").click();
await page.waitForTimeout(1000);
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.getByRole("button", { name: "Playground", exact: true }).click();
expect(await page.locator(".markdown").count()).toBeGreaterThan(0);
expect(await page.getByText("Langflow").count()).toBeGreaterThan(2);
},
);

View File

@ -1,91 +0,0 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { getAllResponseMessage } from "../../utils/get-all-response-message";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
withEventDeliveryModes(
"Image Sentiment Analysis",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await page.goto("/");
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page
.getByText("Image Sentiment Analysis", { exact: true })
.last()
.click();
await initialGPTsetup(page);
//* TODO: Remove these 3 steps once the template is updated *//
await page
.getByTestId("handle-structuredoutput-shownode-structured output-right")
.click();
await page.getByTestId("handle-parser-shownode-json or table-left").click();
await page.getByTestId("tab_1_stringify").click();
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page.waitForSelector('[data-testid="input-chat-playground"]', {
timeout: 100000,
});
// Upload image using the hidden file input
const filePath = path.resolve(__dirname, "../../assets/chain.png");
const fileInput = page.locator('input[type="file"]');
await fileInput.setInputFiles(filePath);
// Wait for file preview to appear (shows loading then the image)
await page.waitForSelector('img[alt="chain.png"]', { timeout: 30000 });
await page.waitForSelector('[data-testid="button-send"]', {
timeout: 100000,
});
await page.getByTestId("button-send").click();
// Wait for the flow to complete
try {
await page.waitForSelector('[data-testid="stop_building_button"]', {
timeout: 30000,
state: "visible",
});
await page.waitForSelector('[data-testid="stop_building_button"]', {
timeout: 180000,
state: "hidden",
});
} catch (_error) {
console.error("Timeout error");
test.skip(true, "Timeout error");
}
// Verify the image is visible in the chat messages after sending
// Note: Server renames file with timestamp prefix (e.g., "2026-02-03_13-55-02_chain.png")
await expect(page.locator('img[alt$="chain.png"]')).toBeVisible();
await page.waitForSelector('[data-testid="div-chat-message"]', {
timeout: 30000,
});
const textContents = await getAllResponseMessage(page);
expect(textContents.length).toBeGreaterThan(10);
expect(textContents.toLowerCase()).toContain("sentiment");
expect(textContents.toLowerCase()).toContain("neutral");
expect(textContents.toLowerCase()).toContain("description");
},
);

View File

@ -1,36 +1,26 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { getAllResponseMessage } from "../../utils/get-all-response-message";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { unselectNodes } from "../../utils/unselect-nodes";
import { waitForOpenModalWithChatInput } from "../../utils/wait-for-open-modal";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { TEXTS } from "../../utils/constants/texts";
test(
"Instagram Copywriter",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
skipIfMissing.openAiKey();
test.skip(
!process?.env?.TAVILY_API_KEY,
"TAVILY_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
loadDotenvIfLocal(__dirname);
await page.goto("/");
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Instagram Copywriter" }).click();
await openStarterProject(page, "Instagram Copywriter");
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
@ -57,11 +47,13 @@ test(
await unselectNodes(page);
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", {
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 30000 * 2,
});
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page
.getByText("Create a Langflow post", { exact: true })
.last()

View File

@ -1,10 +1,10 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { unselectNodes } from "../../utils/unselect-nodes";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { TEXTS } from "../../utils/constants/texts";
test(
"Invoice Summarizer",
{ tag: ["@release", "@starter-projects"] },
@ -15,15 +15,8 @@ test(
!process?.env?.NEEDLE_COLLECTION_ID,
"OPENAI_API_KEY, NEEDLE_API_KEY, and NEEDLE_COLLECTION_ID required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Invoice Summarizer" }).click();
loadDotenvIfLocal(__dirname);
await openStarterProject(page, "Invoice Summarizer");
await initialGPTsetup(page);
@ -53,14 +46,18 @@ test(
await page.getByTestId("button_run_chat output").click();
// Wait for the flow to build successfully
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 120000,
});
// Switch to Playground
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
// Wait for the playground to be ready
const inputPlaceholder = page
.getByPlaceholder("Send a message...", { exact: true })
.getByPlaceholder(TEXTS.placeholderSendMessage, { exact: true })
.last();
await expect(inputPlaceholder).toBeVisible({ timeout: 10000 });

View File

@ -1,36 +1,27 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { getAllResponseMessage } from "../../utils/get-all-response-message";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { disableInspectPanel } from "../../utils/open-advanced-options";
import { unselectNodes } from "../../utils/unselect-nodes";
import { waitForOpenModalWithChatInput } from "../../utils/wait-for-open-modal";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Market Research",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
skipIfMissing.openAiKey();
test.skip(
!process?.env?.TAVILY_API_KEY,
"TAVILY_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
loadDotenvIfLocal(__dirname);
await page.goto("/");
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Market Research" }).click();
await openStarterProject(page, "Market Research");
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
@ -58,13 +49,15 @@ withEventDeliveryModes(
await page.getByTestId("tab_1_stringify").click();
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", {
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 60000 * 3,
});
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByText("No input message provided.", { exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page
.getByText(TEXTS.labelNoInputMessage, { exact: true })
.last()
.isVisible();

View File

@ -1,36 +1,28 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { buildFlowAndWait } from "../../utils/flow/build-flow-and-wait";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Memory Chatbot",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Memory Chatbot" }).click();
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await openStarterProject(page, "Memory Chatbot");
await initialGPTsetup(page);
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.getByRole("button", { name: "Playground", exact: true }).click();
await buildFlowAndWait(page);
await page
.getByText("No input message provided.", { exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page
.getByText(TEXTS.labelNoInputMessage, { exact: true })
.last()
.isVisible();

View File

@ -1,9 +1,9 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
withEventDeliveryModes(
"News Aggregator",
@ -13,21 +13,10 @@ withEventDeliveryModes(
!process?.env?.AGENTQL_API_KEY,
"AGENTQL_API_KEY required to run this test",
);
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await page.goto("/");
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "News Aggregator" }).click();
await openStarterProject(page, "News Aggregator");
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,

View File

@ -1,33 +1,24 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Pokedex Agent",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Pokédex Agent" }).click();
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await openStarterProject(page, "Pokédex Agent");
await initialGPTsetup(page);
await page.getByTestId("playground-btn-flow-io").click();
await page.getByTestId("input-chat-playground").isVisible();
await expect(page.getByTestId("input-chat-playground")).toBeVisible();
await page.getByTestId("input-chat-playground").click();
await page
.getByTestId("input-chat-playground")
@ -35,7 +26,7 @@ withEventDeliveryModes(
await page.getByTestId("button-send").last().click();
const stopButton = page.getByRole("button", { name: "Stop" });
const stopButton = page.getByRole("button", { name: TEXTS.stop });
await stopButton.waitFor({ state: "visible", timeout: 40000 });
if (await stopButton.isVisible()) {

View File

@ -1,79 +0,0 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { uploadFile } from "../../utils/upload-file";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
withEventDeliveryModes(
"Portfolio Website Code Generator",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.ANTHROPIC_API_KEY,
"ANTHROPIC_API_KEY required to run this test",
);
// TODO: remove this skip once the test is stabilized
test.skip(true, "Skipping flaky test until it can be stabilized");
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await page.goto("/");
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page
.getByRole("heading", { name: "Portfolio Website Code Generator" })
.click();
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
});
await initialGPTsetup(page, {
skipAdjustScreenView: true,
skipSelectGptModel: true,
});
await page
.getByTestId("popover-anchor-input-api_key")
.last()
.fill(process.env.ANTHROPIC_API_KEY ?? "");
await page
.getByTestId("popover-anchor-input-api_key")
.first()
.fill(process.env.ANTHROPIC_API_KEY ?? "");
await uploadFile(page, "resume.txt");
await page.getByTestId("playground-btn-flow-io").click();
await page.waitForSelector('[data-testid="button-send"]', {
timeout: 3000,
});
await page.getByTestId("button-send").click();
await page.waitForSelector('[data-testid="chat-code-tab"]', {
timeout: 30000 * 3,
});
await page.waitForSelector(".markdown", { timeout: 30000 });
const textContents = await page
.locator(".markdown")
.last()
.allTextContents();
const concatAllText = textContents.join(" ").toLowerCase();
expect(concatAllText.length).toBeGreaterThan(200);
expect(concatAllText).toContain("div");
expect(concatAllText).toContain("body");
},
);

View File

@ -1,111 +0,0 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
withEventDeliveryModes(
"Price Deal Finder",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.AGENTQL_API_KEY,
"AGENTQL_API_KEY required to run this test",
);
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
test.skip(
!process?.env?.TAVILY_API_KEY,
"TAVILY_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Price Deal Finder" }).click();
await adjustScreenView(page);
await initialGPTsetup(page, {
skipAdjustScreenView: true,
skipAddNewApiKeys: true,
skipSelectGptModel: true,
});
await page
.getByTestId("popover-anchor-input-api_key")
.nth(0)
.fill(process?.env?.TAVILY_API_KEY ?? "");
await page
.getByTestId("popover-anchor-input-api_key")
.nth(1)
.fill(process?.env?.AGENTQL_API_KEY ?? "");
await page
.getByTestId("popover-anchor-input-api_key")
.nth(2)
.fill(process?.env?.OPENAI_API_KEY ?? "");
await page.getByTestId("playground-btn-flow-io").click();
await page.waitForSelector('[data-testid="button-send"]', {
timeout: 3000,
});
const product = randomProduct();
await page.getByTestId("input-chat-playground").fill(product);
await page.getByTestId("button-send").click();
try {
await page.waitForSelector('[data-testid="button-stop"]', {
timeout: 180000,
state: "hidden",
});
} catch (_error) {
console.error("Timeout error");
test.skip(true, "Timeout error");
}
await page.waitForSelector(".markdown", { timeout: 3000 });
const textContents = await page
.locator(".markdown")
.last()
.allTextContents();
const concatAllText = textContents.join(" ").toLowerCase();
expect(concatAllText.length).toBeGreaterThan(100);
const zeldaChapter = product.split(" ")[1];
expect(concatAllText).toContain(zeldaChapter);
},
);
const randomProduct = () => {
const products = [
"Zelda Tears of the Kingdom",
"Zelda Ocarina of Time",
"Zelda Majora's Mask",
"Zelda The Wind Waker",
"Zelda Twilight Princess",
"Zelda Skyward Sword",
"Zelda Breath of the Wild",
"Zelda Link's Awakening",
"Zelda Link to the Past",
];
return products[Math.floor(Math.random() * products.length)].toLowerCase();
};

View File

@ -1,30 +1,21 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { getAllResponseMessage } from "../../utils/get-all-response-message";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { waitForOpenModalWithChatInput } from "../../utils/wait-for-open-modal";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Prompt Chaining",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await page.goto("/");
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Prompt Chaining" }).click();
await openStarterProject(page, "Prompt Chaining");
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
@ -33,11 +24,15 @@ withEventDeliveryModes(
await initialGPTsetup(page);
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", { timeout: 60000 });
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 60000,
});
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByText("No input message provided.", { exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page
.getByText(TEXTS.labelNoInputMessage, { exact: true })
.last()
.isVisible();

View File

@ -1,24 +1,18 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { selectGptModel } from "../../utils/select-gpt-model";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Research Translation Loop.spec",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
@ -50,7 +44,7 @@ withEventDeliveryModes(
await page.getByTestId("button-send").click();
const stopButton = page.getByRole("button", { name: "Stop" });
const stopButton = page.getByRole("button", { name: TEXTS.stop });
await stopButton.waitFor({ state: "visible", timeout: 40000 });
if (await stopButton.isVisible()) {

View File

@ -1,43 +1,35 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { getAllResponseMessage } from "../../utils/get-all-response-message";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { waitForOpenModalWithoutChatInput } from "../../utils/wait-for-open-modal";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { buildFlowAndWait } from "../../utils/flow/build-flow-and-wait";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"SEO Keyword Generator",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await page.goto("/");
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "SEO Keyword Generator" }).click();
await openStarterProject(page, "SEO Keyword Generator");
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
});
await initialGPTsetup(page);
await buildFlowAndWait(page);
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByText("No input message provided.", { exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page
.getByText(TEXTS.labelNoInputMessage, { exact: true })
.last()
.isVisible();

View File

@ -1,43 +1,35 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { getAllResponseMessage } from "../../utils/get-all-response-message";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { waitForOpenModalWithoutChatInput } from "../../utils/wait-for-open-modal";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { buildFlowAndWait } from "../../utils/flow/build-flow-and-wait";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"SaaS Pricing",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await page.goto("/");
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "SaaS Pricing" }).click();
await openStarterProject(page, "SaaS Pricing");
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
});
await initialGPTsetup(page);
await buildFlowAndWait(page);
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByText("No input message provided.", { exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page
.getByText(TEXTS.labelNoInputMessage, { exact: true })
.last()
.isVisible();

View File

@ -1,42 +0,0 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
test.skip(
"Sequential Task Agent",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page
.getByRole("heading", { name: "Sequential Tasks Agent" })
.first()
.click();
await initialGPTsetup(page);
await page.getByTestId("button_run_chat output").click();
await page.waitForTimeout(1000);
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.getByRole("button", { name: "Playground", exact: true }).click();
expect(await page.locator(".markdown").count()).toBeGreaterThan(0);
expect(await page.getByText("Agile").count()).toBeGreaterThan(0);
},
);

View File

@ -1,28 +1,25 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Simple Agent Memory",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await awaitBootstrapTest(page);
// Open Simple Agent template
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Simple Agent" }).first().click();
await page
.getByRole("heading", { name: TEXTS.templateSimpleAgent })
.first()
.click();
await initialGPTsetup(page);
// Open Playground

View File

@ -1,28 +1,18 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Simple Agent",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Simple Agent" }).first().click();
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await openStarterProject(page, "Simple Agent");
await initialGPTsetup(page);
await page.getByTestId("playground-btn-flow-io").click();
@ -34,7 +24,7 @@ withEventDeliveryModes(
await page.getByTestId("button-send").last().click();
const stopButton = page.getByRole("button", { name: "Stop" });
const stopButton = page.getByRole("button", { name: TEXTS.stop });
await stopButton.waitFor({ state: "visible", timeout: 30000 });
if (await stopButton.isVisible()) {

View File

@ -1,10 +1,10 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { TEXTS } from "../../utils/constants/texts";
function getRandomSocialMediaQuery(): string {
const companies = [
"OpenAI",
@ -61,15 +61,8 @@ withEventDeliveryModes(
!process?.env?.APIFY_API_TOKEN,
"APIFY_API_TOKEN required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Social Media Agent" }).click();
loadDotenvIfLocal(__dirname);
await openStarterProject(page, "Social Media Agent");
await initialGPTsetup(page);
@ -98,7 +91,7 @@ withEventDeliveryModes(
await page.getByTestId("button-send").last().click();
const stopButton = page.getByRole("button", { name: "Stop" });
const stopButton = page.getByRole("button", { name: TEXTS.stop });
await stopButton.waitFor({ state: "visible", timeout: 30000 });
if (await stopButton.isVisible()) {

View File

@ -1,25 +1,19 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { unselectNodes } from "../../utils/unselect-nodes";
import { uploadFile } from "../../utils/upload-file";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"user should be able to analyze text sentiment",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
@ -39,9 +33,13 @@ withEventDeliveryModes(
await page.getByText("Expand").click();
await unselectNodes(page);
await page.getByTestId("button_run_chat output").last().click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 120000,
});
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page
.getByText("Add a Chat Input component to your flow to send messages.", {
exact: true,

View File

@ -1,29 +1,22 @@
import { type Page } from "@playwright/test";
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Travel Planning Agent",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
skipIfMissing.openAiKey();
test.skip(
!process?.env?.SEARCH_API_KEY,
"SEARCH_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
loadDotenvIfLocal(__dirname);
await page.goto("/");
await awaitBootstrapTest(page);
@ -64,11 +57,13 @@ withEventDeliveryModes(
test.skip();
}
await page.waitForSelector("text=built successfully", {
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 60000 * 3,
});
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForSelector("text=default session", {
timeout: 30000,

View File

@ -1,25 +1,20 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { getAllResponseMessage } from "../../utils/get-all-response-message";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { waitForOpenModalWithoutChatInput } from "../../utils/wait-for-open-modal";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { buildFlowAndWait } from "../../utils/flow/build-flow-and-wait";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"Twitter Thread Generator",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await page.goto("/");
await awaitBootstrapTest(page);
@ -37,13 +32,13 @@ withEventDeliveryModes(
await page.getByTestId("title-Chat Output").click();
await page.getByTestId("icon-MoreHorizontal").click();
await page.getByText("Expand").click();
await buildFlowAndWait(page);
await page.getByTestId("button_run_chat output").click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByText("No input message provided.", { exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page
.getByText(TEXTS.labelNoInputMessage, { exact: true })
.last()
.isVisible();

View File

@ -1,33 +1,23 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
import { TEXTS } from "../../utils/constants/texts";
withEventDeliveryModes(
"YouTube Analysis",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
skipIfMissing.openAiKey();
test.skip(
!process?.env?.YOUTUBE_API_KEY,
"YOUTUBE_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
loadDotenvIfLocal(__dirname);
await page.goto("/");
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "YouTube Analysis" }).click();
await openStarterProject(page, "YouTube Analysis");
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
@ -42,7 +32,9 @@ withEventDeliveryModes(
await page.getByTestId("button_run_chat output").last().click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 120000,
});
await page.getByTestId("playground-btn-flow-io").click();

View File

@ -1,24 +1,14 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { selectAssistantOpenAiModel } from "../../utils/select-assistant-openai-model";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { openStarterProject } from "../../utils/flow/open-starter-project";
test.describe("Assistant Panel Integration", { tag: ["@release"] }, () => {
test.beforeEach(async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await openStarterProject(page, "Basic Prompting");
// Open assistant panel and wait for model selector
await page.getByTestId("assistant-button").click();

View File

@ -1,17 +1,11 @@
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { openStarterProject } from "../../utils/flow/open-starter-project";
test.describe("Assistant Panel UI", { tag: ["@release"] }, () => {
test("should open and close from canvas controls", async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
skipIfMissing.openAiKey();
await openStarterProject(page, "Basic Prompting");
// Panel should not be visible initially
await expect(page.getByTestId("assistant-panel")).not.toBeVisible();

View File

@ -11,6 +11,7 @@ import {
import { selectGptModel } from "../../utils/select-gpt-model";
import { zoomOut } from "../../utils/zoom-out";
import { TEXTS } from "../../utils/constants/texts";
test(
"should create a flow with decision",
{ tag: ["@release", "@components", "@workflow"] },
@ -34,7 +35,7 @@ test(
//---------------------------------- CHAT INPUT
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat input");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchChatInput);
await page.waitForSelector('[data-testid="input_outputChat Input"]', {
timeout: 2000,
});
@ -157,7 +158,7 @@ test(
});
//---------------------------------- PROMPT
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("prompt");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchPrompt);
await page.waitForSelector(
'[data-testid="models_and_agentsPrompt Template"]',
{
@ -172,7 +173,9 @@ test(
//---------------------------------- OPENAI
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("openai");
await page
.getByTestId("sidebar-search-input")
.fill(TEXTS.providerOpenAiSearch);
await page.waitForSelector('[data-testid="openai_openai_draggable"]', {
timeout: 2000,
});
@ -195,7 +198,7 @@ test(
});
//---------------------------------- CHAT OUTPUT
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat output");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchChatOutput);
await page.waitForSelector('[data-testid="input_outputChat Output"]', {
timeout: 2000,
});
@ -209,7 +212,7 @@ test(
});
//---------------------------------- CHAT OUTPUT
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat output");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchChatOutput);
await page.waitForSelector('[data-testid="input_outputChat Output"]', {
timeout: 2000,
});
@ -244,7 +247,7 @@ test(
User: {user_message}
AI:
`);
await page.getByText("Check & Save").last().click();
await page.getByText(TEXTS.checkAndSave).last().click();
//---------------------------------- MAKE CONNECTIONS
await page
.getByTestId("handle-createlist-shownode-json list-right")
@ -370,7 +373,9 @@ test(
await adjustScreenView(page);
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForSelector('[data-testid="input-chat-playground"]', {
timeout: 100000,
});

View File

@ -7,6 +7,7 @@ import { unselectNodes } from "../../utils/unselect-nodes";
import { updateOldComponents } from "../../utils/update-old-components";
import { zoomOut } from "../../utils/zoom-out";
import { TEXTS } from "../../utils/constants/texts";
test(
"user must be able to check similarity between embedding texts",
{ tag: ["@release", "@components"] },
@ -89,7 +90,7 @@ test(
//seventh component
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("text output");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchTextOutput);
await page.waitForSelector("text=Text Output", {
timeout: 1000,
});
@ -128,7 +129,7 @@ test(
await page
.getByTestId("popover-anchor-input-message")
.first()
.fill("langflow");
.fill(TEXTS.authDefaultCredential);
const firstApiKeyInput = page
.getByTestId("popover-anchor-input-openai_api_key")
@ -249,7 +250,9 @@ test(
await page.getByTestId("button_run_text output").click();
await page.waitForSelector("text=built successfully", { timeout: 120000 });
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 120000,
});
await unselectNodes(page);

View File

@ -1,174 +0,0 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { selectGptModel } from "../../utils/select-gpt-model";
test.skip(
"TextInputOutputComponent",
{ tag: ["@release", "@components"] },
async ({ page }) => {
// commented out because new playground does not support text io yet
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("text input");
await page.waitForTimeout(1000);
await page
.getByTestId("input_outputText Input")
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("openai");
await page.waitForTimeout(1000);
await page
.getByTestId("openaiOpenAI")
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
await adjustScreenView(page);
let visibleElementHandle;
const elementsTextInputOutput = await page
.getByTestId("handle-textinput-shownode-output text-right")
.all();
for (const element of elementsTextInputOutput) {
if (await element.isVisible()) {
visibleElementHandle = element;
break;
}
}
await visibleElementHandle!.waitFor({
state: "visible",
timeout: 30000,
});
await visibleElementHandle!.hover();
await page.mouse.down();
for (const element of elementsTextInputOutput) {
if (await element.isVisible()) {
visibleElementHandle = element;
break;
}
}
await visibleElementHandle!.waitFor({
state: "visible",
timeout: 30000,
});
// Move to the second element
await visibleElementHandle!.hover();
// Release the mouse
await page.mouse.up();
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("text output");
await page
.getByTestId("input_outputText Output")
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
await adjustScreenView(page, { numberOfZoomOut: 6 });
const elementsOpenAiOutput = await page
.getByTestId("handle-openaimodel-shownode-text-right")
.all();
for (const element of elementsOpenAiOutput) {
if (await element.isVisible()) {
visibleElementHandle = element;
break;
}
}
await visibleElementHandle!.waitFor({
state: "visible",
timeout: 30000,
});
// Click and hold on the first element
await visibleElementHandle!.hover();
await page.mouse.down();
const elementTextOutputInput = await page
.getByTestId("handle-textoutput-shownode-inputs-left")
.all();
for (const element of elementTextOutputInput) {
if (await element.isVisible()) {
visibleElementHandle = element;
break;
}
}
await visibleElementHandle!.waitFor({
state: "visible",
timeout: 30000,
});
// Move to the second element
await visibleElementHandle!.hover();
// Release the mouse
await page.mouse.up();
await page
.getByTestId(/^rf__node-TextInput-[a-zA-Z0-9]+$/)
.getByTestId("textarea_str_input_value")
.fill("This is a test!");
let outdatedComponents = await page.getByTestId("update-button").count();
while (outdatedComponents > 0) {
await page.getByTestId("update-button").first().click();
await page.waitForTimeout(1000);
outdatedComponents = await page.getByTestId("update-button").count();
}
let filledApiKey = await page.getByTestId("remove-icon-badge").count();
while (filledApiKey > 0) {
await page.getByTestId("remove-icon-badge").first().click();
await page.waitForTimeout(1000);
filledApiKey = await page.getByTestId("remove-icon-badge").count();
}
const apiKeyInput = page.getByTestId("popover-anchor-input-api_key");
const isApiKeyInputVisible = await apiKeyInput.isVisible();
if (isApiKeyInputVisible) {
await apiKeyInput.fill(process.env.OPENAI_API_KEY ?? "");
}
await selectGptModel(page);
await page.waitForTimeout(1000);
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page.getByTestId("button_run_text_output").click();
await page
.getByTestId(/^rf__node-TextOutput-[a-zA-Z0-9]+$/)
.getByTestId("output-inspection-output message-chatoutput")
.click();
await page.getByText("Run Flow", { exact: true }).click();
await page.waitForTimeout(5000);
let textInputContent = await page
.getByPlaceholder("Enter text...")
.textContent();
expect(textInputContent).toBe("This is a test!");
await page.getByText("Outputs", { exact: true }).nth(1).click();
await page.getByText("Text Output", { exact: true }).nth(2).click();
let contentOutput = await page
.getByPlaceholder("Enter text...")
.inputValue();
expect(contentOutput).not.toBe(null);
await page.keyboard.press("Escape");
await page
.getByTestId(/^rf__node-TextInput-[a-zA-Z0-9]+$/)
.getByTestId("textarea_str_input_value")
.fill("This is a test, again just to be sure!");
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page.getByText("Run Flow", { exact: true }).click();
await page.waitForTimeout(5000);
textInputContent = await page
.getByPlaceholder("Enter text...")
.textContent();
expect(textInputContent).toBe("This is a test, again just to be sure!");
await page.getByText("Outputs", { exact: true }).nth(1).click();
await page.getByText("Text Output", { exact: true }).nth(2).click();
contentOutput = await page.getByPlaceholder("Enter text...").inputValue();
expect(contentOutput).not.toBe(null);
},
);

View File

@ -1,5 +1,6 @@
import { expect, test } from "../../fixtures";
import { TEXTS } from "../../utils/constants/texts";
test(
"user must not be able to login after logout and refresh the page when auto_login is false",
{ tag: ["@release", "@api"] },
@ -30,16 +31,22 @@ test(
await page.goto("/");
await page.waitForSelector("text=sign in to langflow", { timeout: 30000 });
await page.waitForSelector(`text=${TEXTS.authSignInHeader}`, {
timeout: 30000,
});
await page.getByPlaceholder("Username").fill("langflow");
await page.getByPlaceholder("Password").fill("langflow");
await page
.getByPlaceholder(TEXTS.placeholderUsername)
.fill(TEXTS.authDefaultCredential);
await page
.getByPlaceholder(TEXTS.placeholderPassword)
.fill(TEXTS.authDefaultCredential);
await page.evaluate(() => {
sessionStorage.removeItem("testMockAutoLogin");
});
await page.getByRole("button", { name: "Sign In" }).click();
await page.getByRole("button", { name: TEXTS.signIn }).click();
await page.waitForSelector('[data-testid="mainpage_title"]', {
timeout: 30000,
@ -51,13 +58,15 @@ test(
sessionStorage.setItem("testMockAutoLogin", "true");
});
await page.getByText("Logout", { exact: true }).click();
await page.getByText(TEXTS.logout, { exact: true }).click();
await page.waitForTimeout(1000);
await page.reload();
await page.waitForSelector("text=sign in to langflow", { timeout: 30000 });
await page.waitForSelector(`text=${TEXTS.authSignInHeader}`, {
timeout: 30000,
});
const isLoggedIn = await page
.getByTestId("mainpage_title")

View File

@ -1,25 +1,22 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { TEXTS } from "../../utils/constants/texts";
test(
"user must be able to edit an empty prompt",
{ tag: ["@release", "@starter-projects"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 100000,
@ -39,7 +36,7 @@ test(
await page.keyboard.press(`ControlOrMeta+a`);
await page.keyboard.press("Backspace");
await page.getByText("Edit Prompt", { exact: true }).click();
await page.getByText(TEXTS.editPrompt, { exact: true }).click();
await page.getByTestId("edit-prompt-sanitized").last().click();
@ -47,7 +44,7 @@ test(
.getByTestId("modal-promptarea_prompt_template")
.fill("THIS IS A TEST");
await page.getByText("Edit Prompt", { exact: true }).click();
await page.getByText(TEXTS.editPrompt, { exact: true }).click();
let promptSanitizedText = await page
.getByTestId("edit-prompt-sanitized")
@ -61,7 +58,7 @@ test(
await page.keyboard.press(`ControlOrMeta+a`);
await page.keyboard.press("Backspace");
await page.getByText("Edit Prompt", { exact: true }).click();
await page.getByText(TEXTS.editPrompt, { exact: true }).click();
await page.getByTestId("edit-prompt-sanitized").last().click();
@ -69,7 +66,7 @@ test(
.getByTestId("modal-promptarea_prompt_template")
.fill("THIS IS A TEST 2");
await page.getByText("Edit Prompt", { exact: true }).click();
await page.getByText(TEXTS.editPrompt, { exact: true }).click();
promptSanitizedText = await page
.getByTestId("edit-prompt-sanitized")

View File

@ -3,6 +3,7 @@ import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { renameFlow } from "../../utils/rename-flow";
import { TEXTS } from "../../utils/constants/texts";
test(
"should be able to move flow from folder, rename it and be displayed on correct folder",
{ tag: ["@release"] },
@ -34,21 +35,21 @@ test(
await page.getByTestId("add-project-button").click();
let countFolders = await page.getByText("New Project").count();
let countFolders = await page.getByText(TEXTS.labelNewProject).count();
while (countFolders > 1) {
await page.getByText("New Project").first().hover();
await page.getByText(TEXTS.labelNewProject).first().hover();
await page.getByTestId("more-options-button").first().click();
await page.getByTestId("btn-delete-project").click();
await page.getByText("Delete").last().click();
await page.getByText(TEXTS.delete).last().click();
countFolders--;
await page.waitForTimeout(1000);
}
// Get the bounding boxes of the elements
const sourceElement = await page.getByTestId(`card-${randomName}`).first();
const targetElement = await page.getByText("New Project").last();
const targetElement = await page.getByText(TEXTS.labelNewProject).last();
const sourceBox = await sourceElement.boundingBox();
const targetBox = await targetElement.boundingBox();
@ -67,7 +68,7 @@ test(
await page.waitForTimeout(3000);
await page.getByText("New Project").last().click();
await page.getByText(TEXTS.labelNewProject).last().click();
expect(await page.getByTestId(`card-${randomName}`).first().isVisible());
@ -79,7 +80,7 @@ test(
await page.waitForTimeout(3000);
await page.getByText("New Project").last().click();
await page.getByText(TEXTS.labelNewProject).last().click();
expect(
await page.getByTestId(`card-${secondRandomName}`).first().isVisible(),
);
@ -88,7 +89,9 @@ test(
const secondSourceElement = await page
.getByTestId(`card-${secondRandomName}`)
.first();
const secondTargetElement = await page.getByText("New Project").last();
const secondTargetElement = await page
.getByText(TEXTS.labelNewProject)
.last();
const secondSourceBox = await secondSourceElement.boundingBox();
const secondTargetBox = await secondTargetElement.boundingBox();

View File

@ -4,6 +4,7 @@ import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { zoomOut } from "../../utils/zoom-out";
import { TEXTS } from "../../utils/constants/texts";
test(
"should be able to see output preview from grouped components and connect components with a single click",
{ tag: ["@release", "@workspace", "@components"] },
@ -19,7 +20,7 @@ test(
await addLegacyComponents(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("text input");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchTextInput);
await page.waitForSelector('[data-testid="input_outputText Input"]', {
timeout: 3000,
});
@ -262,7 +263,9 @@ test(
await page.getByTestId("button_run_text output").last().click();
await page.waitForSelector("text=built successfully", { timeout: 30000 });
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 30000,
});
expect(
await page
@ -274,9 +277,10 @@ test(
.first()
.click();
await page.getByText("Component Output").isVisible();
const text = await page.getByPlaceholder("Empty").textContent();
await expect(page.getByText(TEXTS.componentOutput)).toBeVisible();
const text = await page
.getByPlaceholder(TEXTS.placeholderEmpty)
.textContent();
const permutations = [
`${randomName}-${secondRandomName}-${thirdRandomName}`,

View File

@ -1,27 +1,24 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { addLegacyComponents } from "../../utils/add-legacy-components";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { TEXTS } from "../../utils/constants/texts";
test(
"user should be able to use chat memory as expected",
{ tag: ["@release", "@workspace", "@components"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
skipIfMissing.openAiKey();
loadDotenvIfLocal(__dirname);
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await adjustScreenView(page);
@ -83,8 +80,8 @@ AI:
await page.getByTestId("button_open_prompt_modal").nth(0).click();
await page.getByTestId("modal-promptarea_prompt_template").fill(prompt);
await page.getByText("Edit Prompt", { exact: true }).click();
await page.getByText("Check & Save").last().click();
await page.getByText(TEXTS.editPrompt, { exact: true }).click();
await page.getByText(TEXTS.checkAndSave).last().click();
await adjustScreenView(page);
@ -98,14 +95,16 @@ AI:
await page.locator('//*[@id="react-flow-id"]').hover();
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForSelector('[data-testid="button-send"]', {
timeout: 100000,
});
await page
.getByPlaceholder("Send a message...")
.getByPlaceholder(TEXTS.placeholderSendMessage)
.fill("hi, my car is blue and I like to eat pizza");
await page.getByTestId("button-send").click();
@ -113,7 +112,7 @@ AI:
await page.waitForSelector("text=AI", { timeout: 30000 });
await page
.getByPlaceholder("Send a message...")
.getByPlaceholder(TEXTS.placeholderSendMessage)
.fill("what color is my car and what do I like to eat?");
await page.getByTestId("button-send").click();

View File

@ -1,15 +1,10 @@
import type { Locator, Page } from "@playwright/test";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
async function addChromaNode(page: Page) {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("Chroma");

View File

@ -2,7 +2,9 @@ import type { Page } from "@playwright/test";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { initialGPTsetup } from "../../utils/initialGPTsetup";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { TEXTS } from "../../utils/constants/texts";
test.describe("Session Deletion Data Leakage Fix", () => {
// Helper to send a message in the playground
async function sendMessage(page: Page, message: string) {
@ -78,21 +80,19 @@ test.describe("Session Deletion Data Leakage Fix", () => {
"should prevent data leakage when default session is deleted and recreated",
{ tag: ["@release", "@regression"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
skipIfMissing.openAiKey();
await awaitBootstrapTest(page);
// Load a starter project
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await initialGPTsetup(page);
// Open playground
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForTimeout(2000);
@ -145,21 +145,19 @@ test.describe("Session Deletion Data Leakage Fix", () => {
"should clear LLM context when session is deleted",
{ tag: ["@release", "@regression"] },
async ({ page }) => {
test.skip(
!process?.env?.OPENAI_API_KEY,
"OPENAI_API_KEY required to run this test",
);
skipIfMissing.openAiKey();
await awaitBootstrapTest(page);
// Load a starter project with memory
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await initialGPTsetup(page);
// Open playground
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForTimeout(2000);

View File

@ -72,15 +72,15 @@ test(
"toggle_bool_add_calculator_tool",
);
await expect(calculatorToggle).toBeVisible({ timeout: 10000 });
expect(await calculatorToggle.isChecked()).toBeTruthy();
await expect(calculatorToggle).toBeChecked();
// S3 — user disables the toggle.
await calculatorToggle.click();
expect(await calculatorToggle.isChecked()).toBeFalsy();
await expect(calculatorToggle).not.toBeChecked();
// Re-enable to confirm the control is bi-directional.
await calculatorToggle.click();
expect(await calculatorToggle.isChecked()).toBeTruthy();
await expect(calculatorToggle).toBeChecked();
},
);

View File

@ -1,20 +1,15 @@
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
import { TEXTS } from "../../utils/constants/texts";
test("chat_io_teste", { tag: ["@release", "@workspace"] }, async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
state: "visible",
});
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat output");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchChatOutput);
await page.waitForSelector('[data-testid="input_outputChat Output"]', {
timeout: 2000,
});
@ -27,7 +22,7 @@ test("chat_io_teste", { tag: ["@release", "@workspace"] }, async ({ page }) => {
});
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat input");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchChatInput);
await page.waitForSelector('[data-testid="input_outputChat Input"]', {
timeout: 2000,
});
@ -49,7 +44,7 @@ test("chat_io_teste", { tag: ["@release", "@workspace"] }, async ({ page }) => {
.click();
await page.getByTestId("handle-chatoutput-noshownode-inputs-target").click();
await page.getByText("Playground", { exact: true }).last().click();
await page.getByText(TEXTS.playground, { exact: true }).last().click();
await page.waitForSelector('[data-testid="input-chat-playground"]', {
timeout: 100000,
});

View File

@ -1,18 +1,13 @@
import { expect } from "@playwright/test";
import { test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
import { TEXTS } from "../../utils/constants/texts";
test(
"CodeAreaModalComponent",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 3000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await page.getByTestId("canvas_controls_dropdown").click();
@ -66,7 +61,7 @@ class CustomComponent(Component):
await page.keyboard.press(`ControlOrMeta+A`);
await page.locator("textarea").fill(codeInputCode);
await page.getByText("Check & Save").last().click();
await page.getByText(TEXTS.checkAndSave).last().click();
await page.getByTestId("div-generic-node").click();

View File

@ -1,6 +1,5 @@
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import {
closeAdvancedOptions,
disableInspectPanel,
@ -8,17 +7,14 @@ import {
openAdvancedOptions,
} from "../../utils/open-advanced-options";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
import { TEXTS } from "../../utils/constants/texts";
test(
"dropDownComponent",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
// Allow for legacy components
await page.getByTestId("sidebar-options-trigger").click();
@ -152,7 +148,6 @@ from langflow.io import BoolInput, DictInput, DropdownInput, StrInput
from langflow.io import MessageInput
from langflow.io import Output
class AmazonBedrockComponent(LCModelComponent):
display_name: str = "Amazon Bedrock"
description: str = "Generate text using Amazon Bedrock LLMs."
@ -248,7 +243,7 @@ class AmazonBedrockComponent(LCModelComponent):
return output
`;
await page.locator("textarea").fill(emptyOptionsCode);
await page.getByRole("button", { name: "Check & Save" }).click();
await page.getByRole("button", { name: TEXTS.checkAndSave }).click();
await page
.getByText("No parameters are available for display.")
.isVisible();

View File

@ -11,6 +11,9 @@ import {
enableInspectPanel,
} from "../../utils/open-advanced-options";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
import { TEXTS } from "../../utils/constants/texts";
// Run tests in this file serially to avoid database conflicts with shared file state
test.describe.configure({ mode: "serial" });
@ -30,13 +33,7 @@ test(
// Read the test file content
const testFilePath = path.join(__dirname, "../../assets/test_file.txt");
const fileContent = fs.readFileSync(testFilePath);
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await disableInspectPanel(page);
@ -88,7 +85,7 @@ test(
),
).toBeVisible();
await page.getByText("My Files").first().hover();
await page.getByText(TEXTS.labelMyFiles).first().hover();
await page.waitForTimeout(500);
await expect(page.getByText(`${sourceFileName}.txt`).last()).toBeVisible({
@ -273,7 +270,7 @@ test(
}
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat output");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchChatOutput);
await page
.getByTestId("input_outputChat Output")
@ -291,7 +288,9 @@ test(
.first()
.click();
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
// Create a new session first
await page.getByTestId("new-chat").click();
@ -387,7 +386,7 @@ test(
.click();
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
// Use the chat header more menu to clear chat (stays in fullscreen)
await page
@ -445,13 +444,7 @@ test(
// Read the test file content
const testFilePath = path.join(__dirname, "../../assets/test_file.txt");
const _fileContent = fs.readFileSync(testFilePath);
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await disableInspectPanel(page);
@ -696,7 +689,7 @@ test(
await awaitBootstrapTest(page, { skipModal: true });
// Navigate to My Files page
await page.getByText("My Files").first().click();
await page.getByText(TEXTS.labelMyFiles).first().click();
// Check if we're on the files page
await page.waitForSelector('[data-testid="mainpage_title"]');
@ -903,7 +896,7 @@ test(
});
await page.getByTestId("output-inspection-file path-file").click();
const filePaths = await page.getByTestId("textarea").textContent();
await page.getByText("Close").last().click();
await page.getByText(TEXTS.close).last().click();
const cleanPath = filePaths
?.replace(/"/g, "")
@ -998,7 +991,9 @@ test(
await textInputs.first().fill(`${folderId}/${file1}.txt`);
await textInputs.last().fill(`${folderId}/${file2}.txt`);
await page.getByRole("button", { name: "Playground", exact: true }).click();
await page
.getByRole("button", { name: TEXTS.playground, exact: true })
.click();
await page.waitForSelector("text=Run Flow", {
timeout: 30000,
@ -1011,7 +1006,7 @@ test(
await expect(page.getByText(fileContent2)).toBeVisible();
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click({ force: true });
// Test Case 2: Single File (clear second input, use only first)
@ -1019,7 +1014,7 @@ test(
await textInputs.first().fill(`${folderId}/${file1}.txt`);
await page
.getByRole("button", { name: "Playground", exact: true })
.getByRole("button", { name: TEXTS.playground, exact: true })
.click({ force: true });
await page.waitForSelector("text=Run Flow", {

View File

@ -1,6 +1,5 @@
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import {
closeAdvancedOptions,
disableInspectPanel,
@ -8,16 +7,12 @@ import {
openAdvancedOptions,
} from "../../utils/open-advanced-options";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
test(
"FloatComponent",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("nvidia");

View File

@ -1,6 +1,5 @@
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import {
closeAdvancedOptions,
disableInspectPanel,
@ -8,16 +7,12 @@ import {
openAdvancedOptions,
} from "../../utils/open-advanced-options";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
test(
"InputComponent",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("Chroma");

View File

@ -8,6 +8,7 @@ import {
openAdvancedOptions,
} from "../../utils/open-advanced-options";
import { TEXTS } from "../../utils/constants/texts";
test(
"InputListComponent",
{ tag: ["@release", "@workspace"] },
@ -16,7 +17,7 @@ test(
await page.getByTestId("blank-flow").click();
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("url");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchUrl);
await page.waitForSelector('[data-testid="data_sourceURL"]', {
timeout: 3000,

View File

@ -1,6 +1,5 @@
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import {
closeAdvancedOptions,
disableInspectPanel,
@ -8,15 +7,15 @@ import {
openAdvancedOptions,
} from "../../utils/open-advanced-options";
test("IntComponent", { tag: ["@release", "@workspace"] }, async ({ page }) => {
await awaitBootstrapTest(page);
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
import { TEXTS } from "../../utils/constants/texts";
test("IntComponent", { tag: ["@release", "@workspace"] }, async ({ page }) => {
await openBlankFlow(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("openai");
await page
.getByTestId("sidebar-search-input")
.fill(TEXTS.providerOpenAiSearch);
await page.waitForSelector('[data-testid="openaiOpenAI"]', {
timeout: 3000,

View File

@ -1,6 +1,5 @@
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import {
closeAdvancedOptions,
disableInspectPanel,
@ -8,16 +7,12 @@ import {
openAdvancedOptions,
} from "../../utils/open-advanced-options";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
test(
"KeypairListComponent",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await disableInspectPanel(page);
@ -43,7 +38,7 @@ test(
await openAdvancedOptions(page);
await page.getByTestId("showmodel_kwargs").click();
expect(await page.getByTestId("showmodel_kwargs").isChecked()).toBeTruthy();
await expect(page.getByTestId("showmodel_kwargs")).toBeChecked();
await closeAdvancedOptions(page);
await adjustScreenView(page, {

View File

@ -2,6 +2,7 @@ import { type Page } from "@playwright/test";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { extractAndCleanCode } from "../../utils/extract-and-clean-code";
// TODO: This test might not be needed anymore
test(
@ -74,27 +75,6 @@ test(
},
);
async function extractAndCleanCode(page: Page): Promise<string> {
const outerHTML = await page
.locator('//*[@id="codeValue"]')
.evaluate((el) => el.outerHTML);
const valueMatch = outerHTML.match(/value="([\s\S]*?)"/);
if (!valueMatch) {
throw new Error("Could not find value attribute in the HTML");
}
const codeContent = valueMatch[1]
.replace(/&quot;/g, '"')
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&#x27;/g, "'")
.replace(/&#x2F;/g, "/");
return codeContent;
}
function updateComponentCode(
code: string,
updates: {

View File

@ -1,7 +1,6 @@
import { expect, test } from "../../fixtures";
import { addLegacyComponents } from "../../utils/add-legacy-components";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import {
closeAdvancedOptions,
disableInspectPanel,
@ -9,16 +8,14 @@ import {
openAdvancedOptions,
} from "../../utils/open-advanced-options";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
import { TEXTS } from "../../utils/constants/texts";
test(
"user should be able to use nested component",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("alter metadata");
@ -57,7 +54,7 @@ test(
expect(await page.getByText("proptest1", { exact: true }).count()).toBe(1);
expect(await page.getByText("proptest2", { exact: true }).count()).toBe(1);
await page.getByText("Save").last().click();
await page.getByText(TEXTS.save).last().click();
await disableInspectPanel(page);
@ -87,7 +84,7 @@ test(
expect(await page.getByText("keytest", { exact: true }).count()).toBe(0);
expect(await page.getByText("proptest", { exact: true }).count()).toBe(0);
await page.getByText("Save", { exact: true }).last().click();
await page.getByText(TEXTS.save, { exact: true }).last().click();
await closeAdvancedOptions(page);

View File

@ -1,7 +1,6 @@
import type { Page } from "@playwright/test";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import {
closeAdvancedOptions,
disableInspectPanel,
@ -9,7 +8,9 @@ import {
openAdvancedOptions,
} from "../../utils/open-advanced-options";
import { unselectNodes } from "../../utils/unselect-nodes";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
import { TEXTS } from "../../utils/constants/texts";
// Helper function to verify prompt variables
async function verifyPromptVariables(
page: Page,
@ -62,13 +63,9 @@ test(
"PromptTemplateComponent - Variable Extraction",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("prompt");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchPrompt);
await page.waitForSelector(
'[data-testid="models_and_agentsPrompt Template"]',
{
@ -150,14 +147,9 @@ test(
"PromptTemplateComponent",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("prompt");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchPrompt);
await page.waitForSelector(
'[data-testid="models_and_agentsPrompt Template"]',
@ -294,7 +286,7 @@ test(
).toBeFalsy();
await page.locator('//*[@id="showprompt"]').click();
expect(await page.locator('//*[@id="showprompt"]').isChecked()).toBeFalsy();
await expect(page.locator('//*[@id="showprompt"]')).not.toBeChecked();
await page.locator('//*[@id="showprompt1"]').click();
expect(
@ -322,7 +314,7 @@ test(
).toBeFalsy();
await page.locator('//*[@id="showprompt"]').click();
expect(await page.locator('//*[@id="showprompt"]').isChecked()).toBeFalsy();
await expect(page.locator('//*[@id="showprompt"]')).not.toBeChecked();
await page.locator('//*[@id="showprompt1"]').click();
expect(
@ -384,14 +376,9 @@ test(
"PromptTemplateComponent - Double Brackets Variable Extraction",
{ tag: ["@release", "@workspace"], timeout: 60000 },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("prompt");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchPrompt);
await page.waitForSelector(
'[data-testid="models_and_agentsPrompt Template"]',
@ -477,14 +464,9 @@ test(
"PromptTemplateComponent - Double Brackets Multiple Variables",
{ tag: ["@release", "@workspace"], timeout: 60000 },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("prompt");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchPrompt);
await page.waitForSelector(
'[data-testid="models_and_agentsPrompt Template"]',
@ -612,14 +594,9 @@ test(
"PromptTemplateComponent - Synchronized State",
{ tag: ["@release", "@workspace"], timeout: 60000 },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("prompt");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchPrompt);
await page.waitForSelector(
'[data-testid="models_and_agentsPrompt Template"]',

View File

@ -9,6 +9,9 @@ import {
openAdvancedOptions,
} from "../../utils/open-advanced-options";
import { extractAndCleanCode } from "../../utils/extract-and-clean-code";
import { TEXTS } from "../../utils/constants/texts";
// TODO: This component doesn't have slider needs updating
test(
"user should be able to use query input",
@ -23,7 +26,9 @@ test(
});
await page.getByTestId("blank-flow").click();
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("openai");
await page
.getByTestId("sidebar-search-input")
.fill(TEXTS.providerOpenAiSearch);
await page.waitForSelector('[data-testid="openaiOpenAI"]', {
timeout: 3000,
@ -129,24 +134,3 @@ test(
await enableInspectPanel(page);
},
);
async function extractAndCleanCode(page: Page): Promise<string> {
const outerHTML = await page
.locator('//*[@id="codeValue"]')
.evaluate((el) => el.outerHTML);
const valueMatch = outerHTML.match(/value="([\s\S]*?)"/);
if (!valueMatch) {
throw new Error("Could not find value attribute in the HTML");
}
const codeContent = valueMatch[1]
.replace(/&quot;/g, '"')
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&#x27;/g, "'")
.replace(/&#x2F;/g, "/");
return codeContent;
}

View File

@ -9,6 +9,7 @@ import {
openAdvancedOptions,
} from "../../utils/open-advanced-options";
import { extractAndCleanCode } from "../../utils/extract-and-clean-code";
// TODO: This component doesn't have slider needs updating
test(
"user should be able to use slider input",
@ -111,44 +112,6 @@ test(
},
);
async function extractAndCleanCode(page: Page): Promise<string> {
// The hidden `#codeValue` mirror is rendered with `<Input value={code} />`,
// i.e. a single-line `<input>` rather than a textarea. Browsers strip
// newlines from `.value` of single-line inputs by HTML spec, so reading
// `(el as HTMLInputElement).value` returns the entire source concatenated
// onto one line. The backend then rejects it as
// `invalid decimal literal (<unknown>, line 1)`.
//
// Read the rendered HTML attribute instead — React serializes the prop as
// `value="..."` with newlines encoded (`&#10;`, `&#13;`, or literal LF
// inside attribute text), and the regex below recovers them faithfully.
// This is the same strategy queryInputComponent.spec.ts uses.
const outerHTML = await page
.locator('//*[@id="codeValue"]')
.evaluate((el) => el.outerHTML);
const valueMatch = outerHTML.match(/value="([\s\S]*?)"/);
if (!valueMatch) {
throw new Error("Could not find value attribute in the #codeValue HTML");
}
const codeContent = valueMatch[1]
.replace(/&quot;/g, '"')
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&#x27;/g, "'")
.replace(/&#x2F;/g, "/")
.replace(/&#10;/g, "\n")
.replace(/&#13;/g, "\r")
.replace(/&#xA;/gi, "\n")
.replace(/&#xD;/gi, "\r");
// Normalize line endings so downstream string operations are OS-agnostic
// (Windows git checkouts of .py files can end up with CRLF).
return codeContent.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
}
// Set the Ace editor's content using the exact same pattern that
// queryInputComponent.spec.ts uses successfully on Windows CI:
// `textarea.last().fill(newCode)` against Ace's hidden text-input textarea.

View File

@ -2,6 +2,7 @@ import { type Page } from "@playwright/test";
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { extractAndCleanCode } from "../../utils/extract-and-clean-code";
test(
"user should interact with tab component",
@ -96,27 +97,6 @@ test(
},
);
async function extractAndCleanCode(page: Page): Promise<string> {
const outerHTML = await page
.locator('//*[@id="codeValue"]')
.evaluate((el) => el.outerHTML);
const valueMatch = outerHTML.match(/value="([\s\S]*?)"/);
if (!valueMatch) {
throw new Error("Could not find value attribute in the HTML");
}
const codeContent = valueMatch[1]
.replace(/&quot;/g, '"')
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&#x27;/g, "'")
.replace(/&#x2F;/g, "/");
return codeContent;
}
function updateComponentCode(
code: string,
updates: {

View File

@ -1,193 +0,0 @@
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { zoomOut } from "../../utils/zoom-out";
test.skip(
"user must be able to interact with table input component",
{
tag: ["@release", "@workspace"],
},
async ({ page }) => {
// SKIP: This test has UI event conflicts where double-click should expose "Input Editor"
// but single-click opens textarea modal that blocks the view. This works in main but
// not in this branch despite no UI code changes. Needs investigation of event handling.
const randomText = Math.random().toString(36).substring(7);
const secondRandomText = Math.random().toString(36).substring(7);
const thirdRandomText = Math.random().toString(36).substring(7);
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await page.waitForSelector(
'[data-testid="sidebar-custom-component-button"]',
{
timeout: 3000,
},
);
await page.waitForSelector('[data-testid="canvas_controls_dropdown"]', {
timeout: 3000,
});
await page.getByTestId("sidebar-custom-component-button").click();
await zoomOut(page, 2);
await page.getByTestId("div-generic-node").click();
await page.getByTestId("code-button-modal").last().click();
const customCodeWithError = `
# from langflow.field_typing import Data
from langflow.custom import Component
from langflow.io import TableInput, Output
from langflow.schema import Data
class CustomComponent(Component):
display_name = "Custom Component"
description = "Use as a template to create your own component."
documentation: str = "https://docs.langflow.org/components-custom-components"
icon = "custom_components"
name = "CustomComponent"
inputs = [
TableInput(
name="input_value",
display_name="Input Value",
value=[
{"alpha": "X1", "bravo": "Y2", "charlie": "Z3", "delta": "W4", "echo": "V5"},
{"alpha": "A6", "bravo": "B7", "charlie": "C8", "delta": "D9", "echo": "E0"},
{"alpha": "F1", "bravo": "G2", "charlie": "H3", "delta": "I4", "echo": "J5"},
{"alpha": "K6", "bravo": "L7", "charlie": "M8", "delta": "N9", "echo": "O0"},
{"alpha": "P1", "bravo": "Q2", "charlie": "R3", "delta": "S4", "echo": "T5"}
],
table_schema=[
{"name": "alpha", "display_name": "Alpha"},
{"name": "bravo", "display_name": "Bravo"},
{"name": "charlie", "display_name": "Charlie"},
{"name": "delta", "display_name": "Delta"},
{"name": "echo", "display_name": "Echo"}
]
)
]
outputs = [
Output(display_name="Output", name="output", method="build_output"),
]
def build_output(self) -> Data:
data = Data(value=self.input_value)
self.status = data
return data
`;
await page.locator("textarea").press(`ControlOrMeta+a`);
await page.locator("textarea").fill(customCodeWithError);
await page.getByText("Check & Save").last().click();
await page.waitForSelector('text="Open table"', {
timeout: 3000,
});
await page.getByText("Open table").click();
await page.waitForSelector(".ag-cell-value", {
timeout: 3000,
});
const visibleTextsGroup1 = ["Alpha", "Bravo", "Charlie", "Delta", "Echo"];
const visibleTextsGroup2 = ["X1", "Y2", "Z3", "W4", "V5"];
const visibleTextsGroup3 = ["P1", "Q2", "R3", "S4", "T5"];
const visibleTextsGroup4 = ["F1", "G2", "H3", "I4", "J5"];
const allVisibleTexts = [
...visibleTextsGroup1,
...visibleTextsGroup2,
...visibleTextsGroup3,
...visibleTextsGroup4,
];
for (const text of allVisibleTexts) {
await expect(page.getByText(text).last()).toBeVisible();
}
await page.locator(".ag-cell-value").first().dblclick({
force: true,
});
await page.getByLabel("Input Editor").fill(randomText);
await page.keyboard.press("Enter");
await page.locator(".ag-cell-value").nth(12).dblclick({
force: true,
});
await page.getByLabel("Input Editor").fill(secondRandomText);
await page.keyboard.press("Enter");
await page.locator(".ag-cell-value").nth(24).dblclick({
force: true,
});
await page.getByLabel("Input Editor").fill(thirdRandomText);
await page.keyboard.press("Enter");
expect(page.getByText(randomText)).toBeVisible();
expect(page.getByText(secondRandomText)).toBeVisible();
expect(page.getByText(thirdRandomText)).toBeVisible();
await page.locator('input[type="checkbox"]').last().click();
await page.getByTestId("icon-Copy").last().click();
await expect(page.getByTestId("duplicate-row-button")).toBeDisabled({
timeout: 1000,
});
let numberOfCopiedRows = await page.getByText(thirdRandomText).count();
expect(numberOfCopiedRows).toBe(2);
await page.locator('input[type="checkbox"]').last().click();
await page.getByTestId("icon-Trash2").last().click();
await expect(page.getByTestId("delete-row-button")).toBeDisabled({
timeout: 1000,
});
await page.locator('input[type="checkbox"]').last().click();
await page.getByTestId("icon-Trash2").click();
numberOfCopiedRows = await page.getByText(thirdRandomText).count();
expect(numberOfCopiedRows).toBe(0);
await page.getByText("Save").last().click();
await page.waitForSelector("text=Open table", {
timeout: 3000,
});
await page.getByText("Open table").click();
await page.waitForSelector(".ag-cell-value", {
timeout: 3000,
});
const visibleTexts = ["Alpha", "Bravo", "Charlie", "Delta", "Echo"];
const notVisibleTexts = ["X1", "thirdRandomText"];
for (const text of visibleTexts) {
const count = await page.getByText(text).count();
expect(count).toBeGreaterThanOrEqual(1);
}
for (const text of notVisibleTexts) {
const count = await page.getByText(text).count();
expect(count).toBe(0);
}
},
);

View File

@ -1,20 +1,16 @@
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
import { TEXTS } from "../../utils/constants/texts";
test(
"TextAreaModalComponent",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("prompt");
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchPrompt);
await page.waitForSelector(
'[data-testid="models_and_agentsPrompt Template"]',

View File

@ -1,6 +1,5 @@
import { expect, test } from "../../fixtures";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import {
closeAdvancedOptions,
disableInspectPanel,
@ -8,16 +7,12 @@ import {
openAdvancedOptions,
} from "../../utils/open-advanced-options";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
test(
"ToggleComponent",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
await page.waitForSelector('[data-testid="blank-flow"]', {
timeout: 30000,
});
await page.getByTestId("blank-flow").click();
await openBlankFlow(page);
// Open the sidebar options dropdown
await page.getByTestId("sidebar-options-trigger").click();
@ -108,7 +103,7 @@ test(
).toBeTruthy();
await page.locator('//*[@id="showpath"]').click();
expect(await page.locator('//*[@id="showpath"]').isChecked()).toBeFalsy();
await expect(page.locator('//*[@id="showpath"]')).not.toBeChecked();
await page.locator('//*[@id="showrecursive"]').click();
expect(
@ -131,7 +126,7 @@ test(
).toBeFalsy();
await page.locator('//*[@id="showpath"]').click();
expect(await page.locator('//*[@id="showpath"]').isChecked()).toBeTruthy();
await expect(page.locator('//*[@id="showpath"]')).toBeChecked();
await page.locator('//*[@id="showrecursive"]').click();
expect(

Some files were not shown because too many files have changed in this diff Show More