import { expect, Page, test } from "@playwright/test"; import uaParser from "ua-parser-js"; // TODO: This test might not be needed anymore test( "user should interact with link component", { tag: ["@release", "@workspace"] }, async ({ context, page }) => { await page.goto("/"); await page.waitForSelector('[data-testid="mainpage_title"]', { timeout: 30000, }); await page.waitForSelector('[id="new-project-btn"]', { timeout: 30000, }); let modalCount = 0; try { const modalTitleElement = await page?.getByTestId("modal-title"); if (modalTitleElement) { modalCount = await modalTitleElement.count(); } } catch (error) { modalCount = 0; } while (modalCount === 0) { await page.getByText("New Flow", { exact: true }).click(); await page.waitForSelector('[data-testid="modal-title"]', { timeout: 3000, }); modalCount = await page.getByTestId("modal-title")?.count(); } const getUA = await page.evaluate(() => navigator.userAgent); const userAgentInfo = uaParser(getUA); 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.getByTestId("sidebar-custom-component-button").click(); await page.getByTitle("fit view").click(); await page.getByTitle("zoom out").click(); await page.getByTestId("title-Custom Component").first().click(); await page.waitForSelector('[data-testid="code-button-modal"]', { timeout: 3000, }); await page.getByTestId("code-button-modal").click(); let cleanCode = await extractAndCleanCode(page); // Use regex pattern to match the imports section more flexibly cleanCode = updateComponentCode(cleanCode, { imports: ["MessageTextInput", "Output", "LinkInput"], inputs: [ { name: "MessageTextInput", config: { name: "input_value", display_name: "Input Value", info: "This is a custom component Input", value: "Hello, World!", tool_mode: true, }, }, { name: "LinkInput", config: { name: "link", display_name: "BUTTON", value: "https://www.datastax.com", text: "Click me", }, }, ], }); await page.locator("textarea").last().press(`ControlOrMeta+a`); await page.keyboard.press("Backspace"); await page.locator("textarea").last().fill(cleanCode); await page.locator('//*[@id="checkAndSaveBtn"]').click(); await page.waitForSelector('[data-testid="fit_view"]', { timeout: 3000, }); await page.getByTestId("fit_view").click(); await page.getByTestId("zoom_out").click(); expect(await page.getByText("BUTTON").isVisible()).toBeTruthy(); expect(await page.getByText("Click me").isVisible()).toBeTruthy(); expect(await page.getByTestId("link_link_link")).toBeEnabled(); await page.getByTestId("link_link_link").click(); }, ); async function extractAndCleanCode(page: Page): Promise { 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"); } let codeContent = valueMatch[1] .replace(/"/g, '"') .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/'/g, "'") .replace(///g, "/"); return codeContent; } function updateComponentCode( code: string, updates: { imports?: string[]; inputs?: Array<{ name: string; config: Record }>; }, ): string { let updatedCode = code; // Update imports if (updates.imports) { const importPattern = /from\s+langflow\.io\s+import\s+([^;\n]+)/; const newImports = updates.imports.join(", "); updatedCode = updatedCode.replace( importPattern, `from langflow.io import ${newImports}`, ); } // Update inputs if (updates.inputs) { const inputsPattern = /inputs\s*=\s*\[([\s\S]*?)\]/; const newInputs = updates.inputs .map(({ name, config }) => { const params = Object.entries(config) .map(([key, value]) => `${key}=${JSON.stringify(value)}`) .join(",\n "); return ` ${name}(\n ${params}\n )`; }) .join(",\n"); updatedCode = updatedCode.replace( inputsPattern, `inputs = [\n${newInputs}\n ]`, ); updatedCode = updatedCode.replace("true", "True"); updatedCode = updatedCode.replace("false", "False"); } return updatedCode; }