feat: Quality of Life improvements for Astra Component (#6953)
* feat: Add helper text for Astra DB embedding generation provider * feat: Add helper text support for dropdown and parameter components * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * Update astradb.py * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * feat: Add org_id to excluded metadata keys in dropdown component * fix: Update Astra DB component UI and starter project configuration - Improved AstraDB component's embedding provider link with better accessibility attributes - Updated Vector Store RAG starter project JSON with formatted configuration - Refined UI text and link presentation for better user experience * Remove providers that arent configured * feat: Add required field indicator to node parameters - Implemented visual indicator (*) for required node parameters - Updated custom parameter title rendering to show required status - Enhanced UI to provide clearer input requirements * Revert "Merge branch 'lfoss-683-1' of https://github.com/langflow-ai/langflow into lfoss-683-1" This reverts commit 2eb0fcb4d3d347de0df1f021f761ea63e882b757, reversing changes made to 9ea44aca477e8a418ef82722da4b1e0fc2afa2e1. * [autofix.ci] apply automated fixes * Add changes back * Fix some comments * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes * Update astradb.py * feat: Add dynamic disabled state for node dialog and int component * feat: Add dynamic placeholder for node dialog input * [autofix.ci] apply automated fixes * feat: Enhance IntComponent with disabled state styling and stepper interactions * Fix some db creation params * Update astradb.py * fix: Resolve node dialog input value handling in TypeScript * Update astradb.py * feat: Add required field validation to node dialog submission * Update astradb.py * [autofix.ci] apply automated fixes * Update astradb.py * Clean up functions and add read only flag * feat: Add readonly support to IntComponent and improve node dialog validation * fix: Improve dropdown component disabled and empty options handling * console.log removed * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * Read only field and cleanup * Astra DB selector updates * [autofix.ci] apply automated fixes * fix: Improve dropdown component disabled state handling * Fix value of region in create db * [autofix.ci] apply automated fixes * Remove database icon from component * fix: Conditionally render dropdown option icons * Don't show Nvidia if not available * [autofix.ci] apply automated fixes * Exceptions unless pending mean, skip * feat: Enhance AstraDBVectorStoreComponent with HTML sanitization - Added DOMPurify for sanitizing HTML input in the convertStringToHTML function. - Introduced a new sanitizeHTML function to ensure safe rendering of HTML strings. * fix: Correct field key usage in NodeDialog and refactor disabled class name in IntComponent - Updated NodeDialogComponent to use fieldKey instead of underscore for better clarity in mapping field values. - Refactored IntComponent to replace the getDisabledClassName function with a constant for the disabled input class, improving readability. * ✨ (playground.spec.ts): Refactor drag and drop functionality to use hover and click for better interaction 🔧 (chatInputOutput.spec.ts): Update drag and drop functionality to use target position for more precise placement 🔧 (chatInputOutputUser-shard-1.spec.ts): Simplify connecting elements by replacing hover and mouse actions with click for better user experience * 🐛 (generalBugs-prompt.spec.ts): fix issue with selecting the last element with getByTestId to ensure correct element is clicked and tested * ✅ (chatInputOutputUser-shard-1.spec.ts): add 600ms timeout to wait for elements to be interactable before proceeding with the test steps --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Eric Hare <ericrhare@gmail.com> Co-authored-by: cristhianzl <cristhian.lousa@gmail.com>
This commit is contained in:
parent
b6333d0e49
commit
4ab4e736be
15 changed files with 1443 additions and 2146 deletions
2731
src/frontend/package-lock.json
generated
2731
src/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -110,6 +110,28 @@ export const NodeDialog: React.FC<NodeDialogProps> = ({
|
|||
};
|
||||
|
||||
const handleSubmitDialog = async () => {
|
||||
// Validate required fields first
|
||||
const missingRequiredFields = Object.entries(dialogTemplate)
|
||||
.filter(
|
||||
([key, fieldValue]) =>
|
||||
(fieldValue as { required: boolean })?.required === true &&
|
||||
(!fieldValues[key] ||
|
||||
(typeof fieldValues[key] === "string" &&
|
||||
fieldValues[key].trim() === "")),
|
||||
)
|
||||
.map(
|
||||
([fieldKey, fieldValue]) =>
|
||||
(fieldValue as { display_name: string })?.display_name || fieldKey,
|
||||
);
|
||||
|
||||
if (missingRequiredFields.length > 0) {
|
||||
handleErrorData({
|
||||
title: "Missing required fields",
|
||||
list: missingRequiredFields,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
await mutateTemplate(
|
||||
|
|
@ -162,13 +184,15 @@ export const NodeDialog: React.FC<NodeDialogProps> = ({
|
|||
<div className="flex flex-col gap-5 overflow-y-auto px-5">
|
||||
{Object.entries(dialogTemplate).map(([fieldKey, fieldValue]) => (
|
||||
<div key={fieldKey}>
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
{getCustomParameterTitle({
|
||||
title:
|
||||
(fieldValue as { display_name: string })?.display_name ??
|
||||
"",
|
||||
nodeId,
|
||||
isFlexView: false,
|
||||
required:
|
||||
(fieldValue as { required: boolean })?.required ?? false,
|
||||
})}
|
||||
</div>
|
||||
<ParameterRenderComponent
|
||||
|
|
@ -178,12 +202,16 @@ export const NodeDialog: React.FC<NodeDialogProps> = ({
|
|||
name={fieldKey}
|
||||
nodeId={nodeId}
|
||||
templateData={fieldValue as Partial<InputFieldType>}
|
||||
templateValue={fieldValues[fieldKey] || ""}
|
||||
templateValue={(fieldValue as { value: string })?.value ?? ""}
|
||||
editNode={false}
|
||||
handleNodeClass={() => {}}
|
||||
nodeClass={dialogNodeData}
|
||||
disabled={false}
|
||||
placeholder=""
|
||||
disabled={
|
||||
(fieldValue as { disabled: boolean })?.disabled ?? false
|
||||
}
|
||||
placeholder={
|
||||
(fieldValue as { placeholder: string })?.placeholder ?? ""
|
||||
}
|
||||
isToolMode={false}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-t
|
|||
import NodeDialog from "@/CustomNodes/GenericNode/components/NodeDialogComponent";
|
||||
import { mutateTemplate } from "@/CustomNodes/helpers/mutate-template";
|
||||
import useAlertStore from "@/stores/alertStore";
|
||||
import { getStatusColor } from "@/utils/stringManipulation";
|
||||
import {
|
||||
convertStringToHTML,
|
||||
getStatusColor,
|
||||
} from "@/utils/stringManipulation";
|
||||
import { PopoverAnchor } from "@radix-ui/react-popover";
|
||||
import Fuse from "fuse.js";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
|
@ -66,7 +69,8 @@ export default function Dropdown({
|
|||
const fuse = new Fuse(validOptions, { keys: ["name", "value"] });
|
||||
const PopoverContentDropdown =
|
||||
children || editNode ? PopoverContent : PopoverContentWithoutPortal;
|
||||
const { nodeClass, nodeId, handleNodeClass, tooltip } = baseInputProps;
|
||||
const { nodeClass, nodeId, handleNodeClass, tooltip, helperText } =
|
||||
baseInputProps;
|
||||
|
||||
// API and store hooks
|
||||
const postTemplateValue = usePostTemplateValue({
|
||||
|
|
@ -79,7 +83,7 @@ export default function Dropdown({
|
|||
// Utility functions
|
||||
const filterMetadataKeys = (
|
||||
metadata: Record<string, any> = {},
|
||||
excludeKeys: string[] = ["api_endpoint", "icon", "status"],
|
||||
excludeKeys: string[] = ["api_endpoint", "icon", "status", "org_id"],
|
||||
) => {
|
||||
return Object.fromEntries(
|
||||
Object.entries(metadata).filter(([key]) => !excludeKeys.includes(key)),
|
||||
|
|
@ -161,7 +165,7 @@ export default function Dropdown({
|
|||
);
|
||||
|
||||
const renderTriggerButton = () => (
|
||||
<ShadTooltip content={!value ? (tooltip as string) : ""}>
|
||||
<div className="flex w-full flex-col">
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
disabled={
|
||||
|
|
@ -199,7 +203,9 @@ export default function Dropdown({
|
|||
className="h-4 w-4"
|
||||
/>
|
||||
)}
|
||||
{value && filteredOptions.includes(value) ? value : placeholderName}{" "}
|
||||
{value && filteredOptions.includes(value)
|
||||
? value
|
||||
: placeholderName}{" "}
|
||||
</span>
|
||||
<ForwardedIconComponent
|
||||
name="ChevronsUpDown"
|
||||
|
|
@ -212,7 +218,12 @@ export default function Dropdown({
|
|||
/>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
</ShadTooltip>
|
||||
{helperText && (
|
||||
<span className="pt-2 text-xs text-muted-foreground">
|
||||
{convertStringToHTML(helperText)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderSearchInput = () => (
|
||||
|
|
@ -302,7 +313,7 @@ export default function Dropdown({
|
|||
data-testid={`${option}-${index}-option`}
|
||||
>
|
||||
<div className="flex w-full items-center gap-2">
|
||||
{optionsMetaData && optionsMetaData.length > 0 && (
|
||||
{optionsMetaData?.[index]?.icon && (
|
||||
<ForwardedIconComponent
|
||||
name={optionsMetaData?.[index]?.icon || "Unknown"}
|
||||
className="h-4 w-4 shrink-0 text-primary"
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export default function IntComponent({
|
|||
disabled,
|
||||
editNode = false,
|
||||
id = "",
|
||||
readonly,
|
||||
}: InputProps<number, IntComponentType>): JSX.Element {
|
||||
const min = -Infinity;
|
||||
// Clear component state
|
||||
|
|
@ -59,6 +60,9 @@ export default function IntComponent({
|
|||
);
|
||||
};
|
||||
|
||||
const DISABLED_INPUT_CLASS =
|
||||
"cursor-default bg-secondary border-border border rounded-md py-2 px-3 text-sm text-input placeholder:text-input";
|
||||
|
||||
const handleNumberChange = (newValue) => {
|
||||
handleOnNewValue({ value: Number(newValue) });
|
||||
};
|
||||
|
|
@ -87,26 +91,35 @@ export default function IntComponent({
|
|||
min={getMinValue()}
|
||||
max={getMaxValue()}
|
||||
onChange={handleNumberChange}
|
||||
isDisabled={disabled || readonly}
|
||||
value={value ?? ""}
|
||||
>
|
||||
<NumberInputField
|
||||
className={getInputClassName()}
|
||||
className={
|
||||
disabled || readonly ? DISABLED_INPUT_CLASS : getInputClassName()
|
||||
}
|
||||
onChange={handleChangeInput}
|
||||
onKeyDown={(event) => handleKeyDown(event, value, "")}
|
||||
onInput={handleInputChange}
|
||||
disabled={disabled}
|
||||
disabled={disabled || readonly}
|
||||
placeholder={editNode ? "Integer number" : "Type an integer number"}
|
||||
data-testid={id}
|
||||
ref={inputRef}
|
||||
/>
|
||||
<NumberInputStepper className={stepperClassName}>
|
||||
<NumberIncrementStepper className={incrementStepperClassName}>
|
||||
<NumberIncrementStepper
|
||||
className={incrementStepperClassName}
|
||||
_disabled={{ cursor: "default" }}
|
||||
>
|
||||
<PlusIcon
|
||||
className={iconClassName}
|
||||
strokeWidth={ICON_STROKE_WIDTH}
|
||||
/>
|
||||
</NumberIncrementStepper>
|
||||
<NumberDecrementStepper className={decrementStepperClassName}>
|
||||
<NumberDecrementStepper
|
||||
className={decrementStepperClassName}
|
||||
_disabled={{ cursor: "default" }}
|
||||
>
|
||||
<MinusIcon
|
||||
className={iconClassName}
|
||||
strokeWidth={ICON_STROKE_WIDTH}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ export function ParameterRenderComponent({
|
|||
disabled,
|
||||
nodeClass,
|
||||
handleNodeClass,
|
||||
helperText: templateData?.helper_text,
|
||||
readonly: templateData.readonly,
|
||||
placeholder,
|
||||
isToolMode,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export type BaseInputProps<valueType = any> = {
|
|||
handleOnNewValue: handleOnNewValueType;
|
||||
disabled: boolean;
|
||||
nodeClass?: APIClassType;
|
||||
helperText?: string;
|
||||
handleNodeClass?: (value: any, code?: string, type?: string) => void;
|
||||
readonly?: boolean;
|
||||
placeholder?: string;
|
||||
|
|
|
|||
|
|
@ -53,10 +53,12 @@ export function getCustomParameterTitle({
|
|||
title,
|
||||
nodeId,
|
||||
isFlexView,
|
||||
required,
|
||||
}: {
|
||||
title: string;
|
||||
nodeId: string;
|
||||
isFlexView: boolean;
|
||||
required: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div className={cn(isFlexView && "max-w-56 truncate")}>
|
||||
|
|
@ -66,6 +68,7 @@ export function getCustomParameterTitle({
|
|||
>
|
||||
{title}
|
||||
</span>
|
||||
{required && <span className="text-red-500">*</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import DOMPurify from "dompurify";
|
||||
import React from "react";
|
||||
import { FieldParserType } from "../types/api";
|
||||
|
||||
function toSnakeCase(str: string): string {
|
||||
|
|
@ -134,3 +136,13 @@ export const getStatusColor = (status: string): string => {
|
|||
|
||||
return "";
|
||||
};
|
||||
|
||||
export const convertStringToHTML = (htmlString: string): JSX.Element => {
|
||||
return React.createElement("span", {
|
||||
dangerouslySetInnerHTML: { __html: sanitizeHTML(htmlString) },
|
||||
});
|
||||
};
|
||||
|
||||
export const sanitizeHTML = (htmlString: string): string => {
|
||||
return DOMPurify.sanitize(htmlString);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,14 +28,11 @@ test(
|
|||
|
||||
await page
|
||||
.getByTestId("outputsChat Output")
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.mouse.up();
|
||||
await page.mouse.down();
|
||||
.hover()
|
||||
.then(async () => {
|
||||
await page.getByTestId("add-component-button-chat-output").click();
|
||||
});
|
||||
|
||||
await adjustScreenView(page);
|
||||
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
|
||||
|
|
@ -47,13 +44,9 @@ test(
|
|||
|
||||
await page
|
||||
.getByTestId("inputsChat Input")
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.mouse.up();
|
||||
await page.mouse.down();
|
||||
|
||||
await page.waitForSelector('[data-testid="fit_view"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
.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");
|
||||
|
|
@ -63,92 +56,21 @@ test(
|
|||
|
||||
await page
|
||||
.getByTestId("outputsText Output")
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.mouse.up();
|
||||
await page.mouse.down();
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'), {
|
||||
targetPosition: { x: 300, y: 300 },
|
||||
});
|
||||
|
||||
await adjustScreenView(page);
|
||||
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page
|
||||
.getByTestId("handle-chatinput-noshownode-message-source")
|
||||
.click();
|
||||
|
||||
const elementsChatInput = await page
|
||||
.locator('[data-testid="handle-chatinput-noshownode-message-source"]')
|
||||
.all();
|
||||
await page.getByTestId("handle-textoutput-shownode-text-left").click();
|
||||
|
||||
let visibleElementHandle;
|
||||
await page.getByTestId("handle-textoutput-shownode-message-right").click();
|
||||
await page.getByTestId("handle-chatoutput-noshownode-text-target").click();
|
||||
|
||||
for (const element of elementsChatInput) {
|
||||
if (await element.isVisible()) {
|
||||
visibleElementHandle = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Click and hold on the first element
|
||||
await visibleElementHandle.hover();
|
||||
await page.mouse.down();
|
||||
|
||||
// Move to the second element
|
||||
|
||||
const elementsTextOutput = await page
|
||||
.getByTestId("handle-textoutput-shownode-text-left")
|
||||
.all();
|
||||
|
||||
for (const element of elementsTextOutput) {
|
||||
if (await element.isVisible()) {
|
||||
visibleElementHandle = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await visibleElementHandle.hover();
|
||||
|
||||
// Release the mouse
|
||||
await page.mouse.up();
|
||||
|
||||
await page.getByTestId("fit_view").click();
|
||||
await page.getByTestId("fit_view").click();
|
||||
await page.getByTestId("fit_view").click();
|
||||
await page.getByTestId("fit_view").click();
|
||||
|
||||
//
|
||||
|
||||
const elementsTextOutputRight = await page
|
||||
.locator('[data-testid="handle-textoutput-shownode-message-right"]')
|
||||
.all();
|
||||
|
||||
for (const element of elementsTextOutputRight) {
|
||||
if (await element.isVisible()) {
|
||||
visibleElementHandle = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Click and hold on the first element
|
||||
await visibleElementHandle.hover();
|
||||
await page.mouse.down();
|
||||
|
||||
//
|
||||
const elementsChatOutput = await page
|
||||
.getByTestId("handle-chatoutput-noshownode-text-target")
|
||||
.all();
|
||||
|
||||
for (const element of elementsChatOutput) {
|
||||
if (await element.isVisible()) {
|
||||
visibleElementHandle = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await visibleElementHandle.hover();
|
||||
|
||||
// Release the mouse
|
||||
await page.mouse.up();
|
||||
|
||||
await page.getByTestId("fit_view").click();
|
||||
await page.getByText("Playground", { exact: true }).last().click();
|
||||
await page.waitForSelector('[data-testid="input-chat-playground"]', {
|
||||
timeout: 100000,
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ test(
|
|||
|
||||
await page.getByText("Edit Prompt", { exact: true }).click();
|
||||
|
||||
await page.getByTestId("edit-prompt-sanitized").click();
|
||||
await page.getByTestId("edit-prompt-sanitized").last().click();
|
||||
|
||||
await page
|
||||
.getByTestId("modal-promptarea_prompt_template")
|
||||
|
|
@ -52,18 +52,19 @@ test(
|
|||
|
||||
let promptSanitizedText = await page
|
||||
.getByTestId("edit-prompt-sanitized")
|
||||
.last()
|
||||
.textContent();
|
||||
|
||||
expect(promptSanitizedText).toBe("THIS IS A TEST");
|
||||
|
||||
await page.getByTestId("edit-prompt-sanitized").click();
|
||||
await page.getByTestId("edit-prompt-sanitized").last().click();
|
||||
|
||||
await page.keyboard.press(`ControlOrMeta+a`);
|
||||
await page.keyboard.press("Backspace");
|
||||
|
||||
await page.getByText("Edit Prompt", { exact: true }).click();
|
||||
|
||||
await page.getByTestId("edit-prompt-sanitized").click();
|
||||
await page.getByTestId("edit-prompt-sanitized").last().click();
|
||||
|
||||
await page
|
||||
.getByTestId("modal-promptarea_prompt_template")
|
||||
|
|
@ -73,6 +74,7 @@ test(
|
|||
|
||||
promptSanitizedText = await page
|
||||
.getByTestId("edit-prompt-sanitized")
|
||||
.last()
|
||||
.textContent();
|
||||
|
||||
expect(promptSanitizedText).toBe("THIS IS A TEST 2");
|
||||
|
|
|
|||
|
|
@ -20,9 +20,10 @@ test("chat_io_teste", { tag: ["@release", "@workspace"] }, async ({ page }) => {
|
|||
|
||||
await page
|
||||
.getByTestId("outputsChat Output")
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.mouse.up();
|
||||
await page.mouse.down();
|
||||
.hover()
|
||||
.then(async () => {
|
||||
await page.getByTestId("add-component-button-chat-output").click();
|
||||
});
|
||||
|
||||
await page.getByTestId("sidebar-search-input").click();
|
||||
await page.getByTestId("sidebar-search-input").fill("chat input");
|
||||
|
|
@ -32,59 +33,19 @@ test("chat_io_teste", { tag: ["@release", "@workspace"] }, async ({ page }) => {
|
|||
|
||||
await page
|
||||
.getByTestId("inputsChat Input")
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.mouse.up();
|
||||
await page.mouse.down();
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'), {
|
||||
targetPosition: { x: 100, y: 100 },
|
||||
});
|
||||
|
||||
await page.waitForSelector('[data-testid="fit_view"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
|
||||
await page.getByTestId("fit_view").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
await page.getByTestId("zoom_out").click();
|
||||
|
||||
const elementsChatInput = await page
|
||||
.locator('[data-testid="handle-chatinput-noshownode-message-source"]')
|
||||
.all();
|
||||
await page.getByTestId("handle-chatinput-noshownode-message-source").click();
|
||||
await page.getByTestId("handle-chatoutput-noshownode-text-target").click();
|
||||
|
||||
let visibleElementHandle;
|
||||
|
||||
for (const element of elementsChatInput) {
|
||||
if (await element.isVisible()) {
|
||||
visibleElementHandle = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Click and hold on the first element
|
||||
await visibleElementHandle.hover();
|
||||
await page.mouse.down();
|
||||
|
||||
// Move to the second element
|
||||
|
||||
const elementsChatOutput = await page
|
||||
.getByTestId("handle-chatoutput-noshownode-text-target")
|
||||
.all();
|
||||
|
||||
for (const element of elementsChatOutput) {
|
||||
if (await element.isVisible()) {
|
||||
visibleElementHandle = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await visibleElementHandle.hover();
|
||||
|
||||
// Release the mouse
|
||||
await page.mouse.up();
|
||||
|
||||
await page.getByTestId("fit_view").click();
|
||||
await page.getByText("Playground", { exact: true }).last().click();
|
||||
await page.waitForSelector('[data-testid="input-chat-playground"]', {
|
||||
timeout: 100000,
|
||||
|
|
|
|||
|
|
@ -95,17 +95,13 @@ test(
|
|||
.getByTestId("inputlist_str_urls_0")
|
||||
.fill("https://www.example.com");
|
||||
|
||||
// Connect text output to first chat output
|
||||
const urlTextOutput = await page
|
||||
.getByTestId("handle-url-shownode-text-right")
|
||||
.nth(0);
|
||||
await urlTextOutput.hover();
|
||||
await page.mouse.down();
|
||||
const firstChatInput = await page
|
||||
await page.getByTestId("handle-url-shownode-text-right").nth(0).click();
|
||||
await page.waitForTimeout(600);
|
||||
|
||||
await page
|
||||
.getByTestId("handle-chatoutput-noshownode-text-target")
|
||||
.nth(0);
|
||||
await firstChatInput.hover();
|
||||
await page.mouse.up();
|
||||
.nth(0)
|
||||
.click();
|
||||
|
||||
// Run flow and test text output inspection
|
||||
await page.getByTestId("button_run_url").first().click();
|
||||
|
|
@ -120,16 +116,16 @@ test(
|
|||
await page.keyboard.press("Escape");
|
||||
|
||||
// Connect dataframe output to second chat output
|
||||
const urlDataframeOutput = await page
|
||||
await page
|
||||
.getByTestId("handle-url-shownode-dataframe-right")
|
||||
.nth(0);
|
||||
await urlDataframeOutput.hover();
|
||||
await page.mouse.down();
|
||||
const secondChatInput = await page
|
||||
.nth(0)
|
||||
.click();
|
||||
await page.waitForTimeout(600);
|
||||
await page
|
||||
.getByTestId("handle-chatoutput-noshownode-text-target")
|
||||
.nth(1);
|
||||
await secondChatInput.hover();
|
||||
await page.mouse.up();
|
||||
.nth(1)
|
||||
.click();
|
||||
await page.waitForTimeout(600);
|
||||
|
||||
// Run and verify text output is still shown
|
||||
await page.getByTestId("button_run_url").first().click();
|
||||
|
|
@ -143,6 +139,7 @@ test(
|
|||
},
|
||||
);
|
||||
await page.keyboard.press("Escape");
|
||||
await page.waitForTimeout(600);
|
||||
|
||||
// Remove text connection
|
||||
const textEdge = await page.locator(".react-flow__edge").first();
|
||||
|
|
@ -162,7 +159,7 @@ test(
|
|||
},
|
||||
);
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
await page.waitForTimeout(600);
|
||||
// Remove all connections
|
||||
const dataEdge = await page.locator(".react-flow__edge").first();
|
||||
await dataEdge.click();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue