Merge remote-tracking branch 'origin/feature/store' into bug/undo-copy

This commit is contained in:
Lucas Oliveira 2023-11-28 10:59:04 -03:00
commit 7ee1ad03f4
101 changed files with 1925 additions and 919 deletions

View file

@ -21,11 +21,17 @@ import PromptAreaComponent from "../../../../components/promptComponent";
import TextAreaComponent from "../../../../components/textAreaComponent";
import ToggleShadComponent from "../../../../components/toggleShadComponent";
import { Button } from "../../../../components/ui/button";
import { TOOLTIP_EMPTY } from "../../../../constants/constants";
import {
LANGFLOW_SUPPORTED_TYPES,
TOOLTIP_EMPTY,
} from "../../../../constants/constants";
import { alertContext } from "../../../../contexts/alertContext";
import { FlowsContext } from "../../../../contexts/flowsContext";
import { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import { postCustomComponentUpdate } from "../../../../controllers/API";
import { ParameterComponentType } from "../../../../types/components";
import { NodeDataType } from "../../../../types/flow";
import {
convertObjToArray,
convertValuesToNumbers,
@ -59,6 +65,7 @@ export default function ParameterComponent({
const ref = useRef<HTMLDivElement>(null);
const refHtml = useRef<HTMLDivElement & ReactNode>(null);
const infoHtml = useRef<HTMLDivElement & ReactNode>(null);
const { setErrorData } = useContext(alertContext);
const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0);
const { setTabsState, tabId, flows } = useContext(FlowsContext);
@ -93,6 +100,25 @@ export default function ParameterComponent({
const { takeSnapshot } = useContext(undoRedoContext);
const handleUpdateValues = async (name: string, data: NodeDataType) => {
const code = data.node?.template["code"]?.value;
if (!code) {
console.error("Code not found in the template");
return;
}
try {
const res = await postCustomComponentUpdate(code, name);
if (res.status === 200 && data.node?.template) {
let clone = cloneDeep(data);
clone.node!.template[name] = res.data.template[name];
setData(clone);
}
} catch (err) {
setErrorData(err as { title: string; list?: Array<string> });
}
};
const handleOnNewValue = (
newValue: string | string[] | boolean | Object[]
): void => {
@ -222,17 +248,7 @@ export default function ParameterComponent({
}, [tooltipTitle, flow]);
return !showNode ? (
left &&
(type === "str" ||
type === "bool" ||
type === "float" ||
type === "code" ||
type === "prompt" ||
type === "file" ||
type === "int" ||
type === "dict" ||
type === "NestedDict") &&
!optionalHandle ? (
left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? (
<></>
) : (
<Button className="h-7 truncate bg-muted p-0 text-sm font-normal text-black hover:bg-muted">
@ -305,17 +321,7 @@ export default function ParameterComponent({
)}
</div>
</div>
{left &&
(type === "str" ||
type === "bool" ||
type === "float" ||
type === "code" ||
type === "prompt" ||
type === "file" ||
type === "int" ||
type === "dict" ||
type === "NestedDict") &&
!optionalHandle ? (
{left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? (
<></>
) : (
<Button className="h-7 truncate bg-muted p-0 text-sm font-normal text-black hover:bg-muted">
@ -409,12 +415,26 @@ export default function ParameterComponent({
) : left === true &&
type === "str" &&
data.node?.template[name].options ? (
<div className="mt-2 w-full">
<Dropdown
options={data.node.template[name].options}
onSelect={handleOnNewValue}
value={data.node.template[name].value ?? "Choose an option"}
></Dropdown>
// TODO: Improve CSS
<div className="mt-2 flex w-full items-center">
<div className="w-5/6 flex-grow">
<Dropdown
options={data.node.template[name].options}
onSelect={handleOnNewValue}
value={data.node.template[name].value ?? "Choose an option"}
id={"dropdown-" + index}
/>
</div>
{data.node?.template[name].refresh && (
<button
className="extra-side-bar-buttons ml-2 mt-1 w-1/6"
onClick={() => {
handleUpdateValues(name, data);
}}
>
<IconComponent name="RefreshCcw" />
</button>
)}
</div>
) : left === true && type === "code" ? (
<div className="mt-2 w-full">
@ -491,6 +511,7 @@ export default function ParameterComponent({
data.node!.template[name].value = newValue;
handleOnNewValue(newValue);
}}
id="div-dict-input"
/>
</div>
) : left === true && type === "dict" ? (

View file

@ -38,7 +38,7 @@ export default function GenericNode({
const { types, deleteNode, reactFlowInstance, setFilterEdge, getFilterEdge } =
useContext(typesContext);
const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
const [inputName, setInputName] = useState(true);
const [inputName, setInputName] = useState(false);
const [nodeName, setNodeName] = useState(data.node!.display_name);
const [inputDescription, setInputDescription] = useState(false);
const [nodeDescription, setNodeDescription] = useState(
@ -206,13 +206,21 @@ export default function GenericNode({
) : (
<ShadTooltip content={data.node?.display_name}>
<div
className="generic-node-tooltip-div text-primary"
className="flex"
onDoubleClick={() => {
setInputName(true);
takeSnapshot();
}}
>
{data.node?.display_name}
<div className="generic-node-tooltip-div pr-3 text-primary">
{data.node?.display_name}
</div>
{data.node?.flow && (
<IconComponent
name="Pencil"
className="h-5 w-5 text-primary-content"
/>
)}
</div>
</ShadTooltip>
)}

View file

@ -11,6 +11,7 @@ const SanitizedHTMLWrapper = ({
return (
<div
data-testid="edit-prompt-sanitized"
className={className}
dangerouslySetInnerHTML={{ __html: sanitizedHTML }}
suppressContentEditableWarning={suppressWarning}

View file

@ -45,6 +45,7 @@ export default function CodeAreaComponent({
<div className="flex w-full items-center">
<span
id={id}
data-testid={id}
className={
editNode
? "input-edit-node input-dialog"

View file

@ -27,6 +27,7 @@ import {
TabsList,
TabsTrigger,
} from "../../components/ui/tabs";
import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants";
import { darkContext } from "../../contexts/darkContext";
import { typesContext } from "../../contexts/typesContext";
import { codeTabsPropsType } from "../../types/components";
@ -242,24 +243,10 @@ export default function CodeTabsComponent({
templateField.charAt(0) !== "_" &&
node.data.node.template[templateField]
.show &&
(node.data.node.template[templateField]
.type === "str" ||
LANGFLOW_SUPPORTED_TYPES.has(
node.data.node.template[templateField]
.type === "bool" ||
node.data.node.template[templateField]
.type === "float" ||
node.data.node.template[templateField]
.type === "code" ||
node.data.node.template[templateField]
.type === "prompt" ||
node.data.node.template[templateField]
.type === "file" ||
node.data.node.template[templateField]
.type === "int" ||
node.data.node.template[templateField]
.type === "dict" ||
node.data.node.template[templateField]
.type === "NestedDict")
.type
)
)
.map((templateField, indx) => {
return (

View file

@ -10,6 +10,7 @@ export default function DictComponent({
onChange,
disabled,
editNode = false,
id = "",
}: DictComponentType): JSX.Element {
useEffect(() => {
if (disabled) {
@ -30,7 +31,7 @@ export default function DictComponent({
)}
>
{
<div className="flex w-full gap-3">
<div className="flex w-full gap-3" data-testid={id}>
<DictAreaModal
value={ref.current}
onChange={(obj) => {
@ -45,6 +46,7 @@ export default function DictComponent({
: "input-disable pointer-events-none cursor-pointer"
}
placeholder="Click to edit your dictionary..."
data-testid="dict-input"
/>
</DictAreaModal>
</div>

View file

@ -11,6 +11,7 @@ export default function Dropdown({
editNode = false,
numberOfOptions = 0,
apiModal = false,
id = "",
}: DropDownComponentType): JSX.Element {
let [internalValue, setInternalValue] = useState(
value === "" || !value ? "Choose an option" : value
@ -31,15 +32,19 @@ export default function Dropdown({
>
{({ open }) => (
<>
<div className={editNode ? "mt-1" : "relative mt-1"}>
<div className={"relative mt-1"}>
<Listbox.Button
data-test={`${id ?? ""}`}
className={
editNode
? "dropdown-component-outline"
: "dropdown-component-false-outline"
}
>
<span className="dropdown-component-display">
<span
className="dropdown-component-display"
data-testid={`${id ?? ""}-display`}
>
{internalValue}
</span>
<span className={"dropdown-component-arrow"}>
@ -63,7 +68,7 @@ export default function Dropdown({
editNode
? "dropdown-component-true-options nowheel custom-scroll"
: "dropdown-component-false-options nowheel custom-scroll",
apiModal ? "mb-2 w-[250px]" : "absolute"
apiModal ? "mb-2 w-[250px]" : "absolute w-full"
)}
>
{options.map((option, id) => (
@ -86,6 +91,7 @@ export default function Dropdown({
selected ? "font-semibold" : "font-normal",
"block truncate "
)}
data-testid={`${option}-${id ?? ""}-option`}
>
{option}
</span>

View file

@ -3,7 +3,10 @@ import { IconComponentProps } from "../../types/components";
import { nodeIconsLucide } from "../../utils/styleUtils";
const ForwardedIconComponent = forwardRef(
({ name, className, iconColor, stroke }: IconComponentProps, ref) => {
(
{ name, className, iconColor, stroke, id = "" }: IconComponentProps,
ref
) => {
const TargetIcon = nodeIconsLucide[name] ?? nodeIconsLucide["unknown"];
return (
<TargetIcon
@ -12,6 +15,7 @@ const ForwardedIconComponent = forwardRef(
style={iconColor ? { color: iconColor } : {}}
ref={ref}
stroke={stroke ? stroke : "currentColor"}
data-testid={id ? `${id}-${name}` : "icon-" + name}
/>
);
}

View file

@ -53,7 +53,7 @@ export default function KeypairListComponent({
return (
<div key={idx} className="flex w-full gap-2">
<Input
id={"keypair" + index}
id={editNode ? "editNodekeypair" + index : "keypair" + index}
type="text"
value={key.trim()}
className={classNames(
@ -71,7 +71,11 @@ export default function KeypairListComponent({
/>
<Input
id={"keypair" + (index + 100).toString()}
id={
editNode
? "editNodekeypair" + (index + 100).toString()
: "keypair" + (index + 100).toString()
}
type="text"
value={obj[key]}
className={editNode ? "input-edit-node" : ""}
@ -88,7 +92,11 @@ export default function KeypairListComponent({
newInputList.push({ "": "" });
onChange(newInputList);
}}
id={"plusbtn" + index.toString()}
id={
editNode
? "editNodeplusbtn" + index.toString()
: "plusbtn" + index.toString()
}
>
<IconComponent
name="Plus"
@ -102,7 +110,11 @@ export default function KeypairListComponent({
newInputList.splice(index, 1);
onChange(newInputList);
}}
id={"minusbtn" + index.toString()}
id={
editNode
? "editNodeminusbtn" + index.toString()
: "minusbtn" + index.toString()
}
>
<IconComponent
name="X"

View file

@ -53,6 +53,7 @@ export default function PromptAreaComponent({
<div className="flex w-full items-center">
<span
id={id}
data-testid={id}
className={
editNode
? "input-edit-node input-dialog"
@ -64,6 +65,7 @@ export default function PromptAreaComponent({
</span>
{!editNode && (
<IconComponent
id={id}
name="ExternalLink"
className={
"icons-parameters-comp" +

View file

@ -47,6 +47,7 @@ export default function TextAreaComponent({
/>
{!editNode && (
<IconComponent
id={id}
name="ExternalLink"
className={
"icons-parameters-comp" +

View file

@ -663,3 +663,15 @@ export const LAST_USED_SPAN_1 = "The last time this key was used.";
export const LAST_USED_SPAN_2 =
"Accurate to within the hour from the most recent usage.";
export const LANGFLOW_SUPPORTED_TYPES = new Set([
"str",
"bool",
"float",
"code",
"prompt",
"file",
"int",
"dict",
"NestedDict",
]);

View file

@ -56,7 +56,11 @@ const FlowsContextInitialValue: FlowsContextType = {
isLoading: true,
flows: [],
removeFlow: (id: string) => {},
addFlow: async (newProject: boolean, flowData?: FlowType) => "",
addFlow: async (
newProject: boolean,
flowData?: FlowType,
override?: boolean
) => "",
updateFlow: (newFlow: FlowType) => {},
incrementNodeId: () => uid(),
downloadFlow: (flow: FlowType) => {},
@ -78,7 +82,7 @@ const FlowsContextInitialValue: FlowsContextType = {
selection: { nodes: any; edges: any },
position: { x: number; y: number; paneX?: number; paneY?: number }
) => {},
saveComponent: async (component: NodeDataType) => "",
saveComponent: async (component: NodeDataType, override: boolean) => "",
deleteComponent: (key: string) => {},
version: "",
};
@ -496,7 +500,8 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
const addFlow = async (
newProject: Boolean,
flow?: FlowType
flow?: FlowType,
override?: boolean
): Promise<String | undefined> => {
if (newProject) {
let flowData = flow
@ -505,6 +510,15 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
// Create a new flow with a default name if no flow is provided.
if (override) {
deleteComponent(flow!.name);
const newFlow = createNewFlow(flowData, flow!);
const { id } = await saveFlowToDatabase(newFlow);
newFlow.id = id;
addFlowToLocalState(newFlow);
return;
}
const newFlow = createNewFlow(flowData, flow!);
const newName = addVersionToDuplicates(newFlow, flows);
@ -595,14 +609,16 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
newFlows[index].data = newFlow.data;
newFlows[index].name = newFlow.name;
}
newFlow = {
...newFlow,
};
return newFlows;
});
}
async function saveFlow(newFlow: FlowType, silent?: boolean) {
console.log(newFlow);
if (newFlow?.data?.nodes?.length === 0) return;
try {
// updates flow in db
const updatedFlow = await updateFlowInDatabase(newFlow);
@ -615,14 +631,10 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
const newFlows = [...prevState];
const index = newFlows.findIndex((flow) => flow.id === newFlow.id);
if (index !== -1) {
newFlows[index] = {
...newFlows[index],
description: updatedFlow.description,
data: updatedFlow.data,
name: updatedFlow.name,
};
newFlows[index].description = newFlow.description ?? "";
newFlows[index].data = newFlow.data;
newFlows[index].name = newFlow.name;
}
return newFlows;
});
//update tabs state
@ -644,9 +656,9 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
}
}
function saveComponent(component: NodeDataType) {
function saveComponent(component: NodeDataType, override: boolean) {
component.node!.official = false;
return addFlow(true, createFlowComponent(component, version));
return addFlow(true, createFlowComponent(component, version), override);
}
function deleteComponent(key: string) {

View file

@ -360,6 +360,16 @@ export async function postCustomComponent(
return await api.post(`${BASE_URL_API}custom_component`, { code });
}
export async function postCustomComponentUpdate(
code: string,
field: string
): Promise<AxiosResponse<APIClassType>> {
return await api.post(`${BASE_URL_API}custom_component/update`, {
code,
field,
});
}
export async function onLogin(user: LoginType) {
try {
const response = await api.post(

View file

@ -13,7 +13,10 @@ import {
// import "ace-builds/webpack-resolver";
import CodeTabsComponent from "../../components/codeTabsComponent";
import IconComponent from "../../components/genericIconComponent";
import { EXPORT_CODE_DIALOG } from "../../constants/constants";
import {
EXPORT_CODE_DIALOG,
LANGFLOW_SUPPORTED_TYPES,
} from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { TemplateVariableType } from "../../types/api";
@ -99,15 +102,9 @@ const ApiModal = forwardRef(
(templateField) =>
templateField.charAt(0) !== "_" &&
node.data.node.template[templateField].show &&
(node.data.node.template[templateField].type === "str" ||
node.data.node.template[templateField].type === "bool" ||
node.data.node.template[templateField].type === "float" ||
node.data.node.template[templateField].type === "code" ||
node.data.node.template[templateField].type === "prompt" ||
node.data.node.template[templateField].type === "file" ||
node.data.node.template[templateField].type === "int" ||
node.data.node.template[templateField].type === "dict" ||
node.data.node.template[templateField].type === "NestedDict")
LANGFLOW_SUPPORTED_TYPES.has(
node.data.node.template[templateField].type
)
)
.map((n, i) => {
arrNodesWithValues.push(node["id"]);
@ -146,9 +143,9 @@ const ApiModal = forwardRef(
);
if (existingTweak) {
existingTweak[tw][template["name"]] = changes as string;
existingTweak[tw][template["name"]!] = changes as string;
if (existingTweak[tw][template["name"]] == template.value) {
if (existingTweak[tw][template["name"]!] == template.value) {
tweak.current.forEach((element) => {
if (element[tw] && Object.keys(element[tw])?.length === 0) {
tweak.current = tweak.current.filter((obj) => {
@ -161,7 +158,7 @@ const ApiModal = forwardRef(
} else {
const newTweak = {
[tw]: {
[template["name"]]: changes,
[template["name"]!]: changes,
},
} as uniqueTweakType;
tweak.current.push(newTweak);

View file

@ -36,10 +36,15 @@ function ConfirmationModal({
size,
open,
onClose,
onCancel,
}: ConfirmationModalType) {
const Icon: any = nodeIconsLucide[icon];
const [modalOpen, setModalOpen] = useState(open ?? false);
useEffect(() => {
if (open) setModalOpen(open);
}, [open]);
useEffect(() => {
if (onClose) onClose!(modalOpen);
}, [modalOpen]);
@ -86,6 +91,7 @@ function ConfirmationModal({
className="mt-5"
variant="outline"
onClick={() => {
if (onCancel) onCancel();
setModalOpen(false);
}}
>

View file

@ -25,7 +25,10 @@ import {
TableHeader,
TableRow,
} from "../../components/ui/table";
import { limitScrollFieldsModal } from "../../constants/constants";
import {
LANGFLOW_SUPPORTED_TYPES,
limitScrollFieldsModal,
} from "../../constants/constants";
import { FlowsContext } from "../../contexts/flowsContext";
import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow";
@ -123,7 +126,7 @@ const EditNodeModal = forwardRef(
"edit-node-modal-box",
nodeLength > limitScrollFieldsModal
? "overflow-scroll overflow-x-hidden custom-scroll"
: "overflow-hidden"
: ""
)}
>
{nodeLength > 0 && (
@ -144,24 +147,9 @@ const EditNodeModal = forwardRef(
(templateParam) =>
templateParam.charAt(0) !== "_" &&
myData.node?.template[templateParam].show &&
(myData.node.template[templateParam]
.type === "str" ||
myData.node.template[templateParam]
.type === "bool" ||
myData.node.template[templateParam]
.type === "float" ||
myData.node.template[templateParam]
.type === "code" ||
myData.node.template[templateParam]
.type === "prompt" ||
myData.node.template[templateParam]
.type === "file" ||
myData.node.template[templateParam]
.type === "int" ||
myData.node.template[templateParam]
.type === "dict" ||
myData.node.template[templateParam]
.type === "NestedDict")
LANGFLOW_SUPPORTED_TYPES.has(
myData.node.template[templateParam].type
)
)
.map((templateParam, index) => (
<TableRow key={index} className="h-10">
@ -261,7 +249,7 @@ const EditNodeModal = forwardRef(
value={
myData.node!.template[
templateParam
].value.toString() === "{}"
]?.value?.toString() === "{}"
? {
yourkey: "value",
}
@ -275,6 +263,7 @@ const EditNodeModal = forwardRef(
].value = newValue;
handleOnNewValue(newValue, templateParam);
}}
id="editnode-div-dict-input"
/>
</div>
) : myData.node?.template[templateParam]
@ -380,6 +369,7 @@ const EditNodeModal = forwardRef(
templateParam
].value ?? "Choose an option"
}
id={"dropdown-edit-" + index}
></Dropdown>
</div>
) : myData.node?.template[templateParam]

View file

@ -54,6 +54,7 @@ export default function DictAreaModal({
/>
<div className="flex h-fit w-full justify-end">
<Button
data-testid="save-dict-button"
className="mt-3"
type="submit"
onClick={() => {

View file

@ -213,6 +213,7 @@ export default function GenericModal({
{type === TypeModal.PROMPT && isEdit && !readonly ? (
<Textarea
id={"modal-" + id}
data-testid={"modal-" + id}
ref={divRefPrompt}
className="form-input h-full w-full rounded-lg custom-scroll focus-visible:ring-1"
value={inputValue}
@ -252,6 +253,8 @@ export default function GenericModal({
handleKeyDown(e, value, "");
}}
readOnly={readonly}
id={"text-area-modal"}
data-testid={"text-area-modal"}
/>
) : (
<></>
@ -308,6 +311,7 @@ export default function GenericModal({
)}
</div>
<Button
data-testid="genericModalBtnSave"
id="genericModalBtnSave"
disabled={readonly}
onClick={() => {

View file

@ -6,6 +6,7 @@ import { Button } from "../../components/ui/button";
import { Checkbox } from "../../components/ui/checkbox";
import { alertContext } from "../../contexts/alertContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { StoreContext } from "../../contexts/storeContext";
import {
getStoreComponents,
getStoreTags,
@ -22,14 +23,17 @@ export default function ShareModal({
children,
open,
setOpen,
disabled,
}: {
children?: ReactNode;
is_component: boolean;
component: FlowType;
open?: boolean;
setOpen?: (open: boolean) => void;
disabled?: boolean;
}): JSX.Element {
const { version, addFlow } = useContext(FlowsContext);
const { hasApiKey } = useContext(StoreContext);
const { setSuccessData, setErrorData } = useContext(alertContext);
const [checked, setChecked] = useState(true);
const [name, setName] = useState(component?.name ?? "");
@ -46,10 +50,12 @@ export default function ShareModal({
useEffect(() => {
if (open || internalOpen) {
handleGetTags();
handleGetNames();
if (hasApiKey) {
handleGetTags();
handleGetNames();
}
}
}, [open, internalOpen]);
}, [open, internalOpen, hasApiKey]);
function handleGetTags() {
setLoadingTags(true);
@ -113,7 +119,7 @@ export default function ShareModal({
return (
<BaseModal
size="smaller-h-full"
open={open ?? internalOpen}
open={(!disabled && open) ?? internalOpen}
setOpen={setOpen ?? internalSetOpen}
>
<BaseModal.Trigger>{children ? children : <></>}</BaseModal.Trigger>

View file

@ -70,6 +70,7 @@ export default function Page({
saveFlow,
setTabsState,
tabId,
flows,
} = useContext(FlowsContext);
const {
types,
@ -144,7 +145,6 @@ export default function Page({
document.removeEventListener("mousemove", handleMouseMove);
};
}, [position, lastCopiedSelection, lastSelection]);
const [selectionMenuVisible, setSelectionMenuVisible] = useState(false);
const { setExtraComponent, setExtraNavigation } = useContext(locationContext);
@ -181,6 +181,8 @@ export default function Page({
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const index = flows.findIndex((flowId) => flowId.id === flow.id);
const interval = setInterval(() => {
setSeconds((prevSeconds) => {
let updatedSeconds = prevSeconds + 1;
@ -188,7 +190,7 @@ export default function Page({
if (updatedSeconds % 30 === 0) {
saveFlow(
{
...flow!,
...flows[index]!,
data: reactFlowInstance
? reactFlowInstance!.toObject()
: flow!.data,
@ -318,9 +320,9 @@ export default function Page({
);
// Calculate the position where the node should be created
const position = reactFlowInstance!.project({
x: event.clientX - reactflowBounds!.left,
y: event.clientY - reactflowBounds!.top,
const position = reactFlowInstance!.screenToFlowPosition({
x: event.clientX,
y: event.clientY,
});
// Generate a unique node ID

View file

@ -6,6 +6,7 @@ import { Input } from "../../../../components/ui/input";
import { Separator } from "../../../../components/ui/separator";
import { alertContext } from "../../../../contexts/alertContext";
import { FlowsContext } from "../../../../contexts/flowsContext";
import { StoreContext } from "../../../../contexts/storeContext";
import { typesContext } from "../../../../contexts/typesContext";
import ApiModal from "../../../../modals/ApiModal";
import ExportModal from "../../../../modals/exportModal";
@ -29,6 +30,7 @@ export default function ExtraSidebar(): JSX.Element {
useContext(typesContext);
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt, version } =
useContext(FlowsContext);
const { hasApiKey, validApiKey } = useContext(StoreContext);
const { setErrorData } = useContext(alertContext);
const [dataFilter, setFilterData] = useState(data);
const [search, setSearch] = useState("");
@ -179,25 +181,56 @@ export default function ExtraSidebar(): JSX.Element {
}, [getFilterEdge, data]);
const ModalMemo = useMemo(
() => (
<ShareModal is_component={false} component={flow!}>
<ShadTooltip content="Share" side="top">
<div className={classNames("extra-side-bar-buttons")}>
<IconComponent name="Share2" className="side-bar-button-size" />
</div>
</ShadTooltip>
</ShareModal>
),
[]
() =>
!hasApiKey || !validApiKey ? (
<button
disabled={!hasApiKey || !validApiKey}
className={classNames(
"extra-side-bar-buttons",
!hasApiKey || !validApiKey ? "button-disable cursor-default" : ""
)}
>
<IconComponent
name="Share2"
className={classNames(
"side-bar-button-size",
!hasApiKey || !validApiKey ? "extra-side-bar-save-disable" : ""
)}
/>
</button>
) : (
<ShareModal
is_component={false}
component={flow!}
disabled={!hasApiKey || !validApiKey}
>
<button
disabled={!hasApiKey || !validApiKey}
className={classNames(
"extra-side-bar-buttons",
!hasApiKey || !validApiKey ? "button-disable cursor-default" : ""
)}
>
<IconComponent
name="Share2"
className={classNames(
"side-bar-button-size",
!hasApiKey || !validApiKey ? "extra-side-bar-save-disable" : ""
)}
/>
</button>
</ShareModal>
),
[hasApiKey, validApiKey]
);
const ExportMemo = useMemo(
() => (
<ExportModal>
<ShadTooltip content="Export" side="top">
<div className={classNames("extra-side-bar-buttons")}>
<button className={classNames("extra-side-bar-buttons")}>
<IconComponent name="FileDown" className="side-bar-button-size" />
</div>
</button>
</ShadTooltip>
</ExportModal>
),
@ -276,8 +309,9 @@ export default function ExtraSidebar(): JSX.Element {
</div>
</ShadTooltip>
</div>
<div className="side-bar-button">{ModalMemo}</div>
<ShadTooltip content="Share" side="top" styleClasses="cursor-default">
<div className="side-bar-button">{ModalMemo}</div>
</ShadTooltip>
</div>
<Separator />
<div className="side-bar-search-div-placement">
@ -305,7 +339,15 @@ export default function ExtraSidebar(): JSX.Element {
<div className="side-bar-components-div-arrangement">
{Object.keys(dataFilter)
.sort()
.sort((a, b) => {
if (a.toLowerCase() === "saved_components") {
return -1;
} else if (b.toLowerCase() === "saved_components") {
return 1;
} else {
return a.localeCompare(b);
}
})
.map((SBSectionName: keyof APIObjectType, index) =>
Object.keys(dataFilter[SBSectionName]).length > 0 ? (
<DisclosureComponent

View file

@ -88,17 +88,17 @@ export default function SidebarDraggableComponent({
}}
>
<div
data-testid={sectionName + display_name}
id={sectionName + display_name}
className="side-bar-components-div-form"
>
<span className="side-bar-components-text">{display_name}</span>
<div>
<SelectTrigger>
<IconComponent
name="Menu"
className="side-bar-components-icon "
/>
</SelectTrigger>
<IconComponent
name="Menu"
className="side-bar-components-icon "
/>
<SelectTrigger></SelectTrigger>
<SelectContent>
<SelectItem value={"download"}>
<div className="flex">

View file

@ -10,6 +10,8 @@ import {
SelectTrigger,
} from "../../../../components/ui/select-custom";
import { FlowsContext } from "../../../../contexts/flowsContext";
import { StoreContext } from "../../../../contexts/storeContext";
import ConfirmationModal from "../../../../modals/ConfirmationModal";
import EditNodeModal from "../../../../modals/EditNodeModal";
import ShareModal from "../../../../modals/shareModal";
import { nodeToolbarPropsType } from "../../../../types/components";
@ -49,6 +51,7 @@ export default function NodeToolbarComponent({
);
const updateNodeInternals = useUpdateNodeInternals();
const { getNodeId } = useContext(FlowsContext);
const { hasApiKey, validApiKey } = useContext(StoreContext);
function canMinimize() {
let countHandles: number = 0;
@ -61,17 +64,18 @@ export default function NodeToolbarComponent({
const isMinimal = canMinimize();
const isGroup = data.node?.flow ? true : false;
const { paste, saveComponent, version } = useContext(FlowsContext);
const { paste, saveComponent, version, flows } = useContext(FlowsContext);
const reactFlowInstance = useReactFlow();
const [showModalAdvanced, setShowModalAdvanced] = useState(false);
const [showconfirmShare, setShowconfirmShare] = useState(false);
const [selectedValue, setSelectedValue] = useState("");
const [showOverrideModal, setShowOverrideModal] = useState(false);
const [flowComponent, setFlowComponent] = useState<FlowType>();
useEffect(() => {
setFlowComponent(createFlowComponent(cloneDeep(data), version));
}, [data]);
}, [data, showModalAdvanced]);
const handleSelectChange = (event) => {
switch (event) {
@ -86,10 +90,10 @@ export default function NodeToolbarComponent({
downloadNode(createFlowComponent(cloneDeep(data), version));
break;
case "Share":
setShowconfirmShare(true);
if (hasApiKey) setShowconfirmShare(true);
break;
case "SaveAll":
saveComponent(cloneDeep(data));
saveComponent(cloneDeep(data), false);
break;
case "disabled":
break;
@ -97,9 +101,16 @@ export default function NodeToolbarComponent({
updateFlowPosition(position, data.node?.flow!);
expandGroupNode(data, reactFlowInstance, getNodeId);
break;
case "override":
setShowOverrideModal(true);
break;
}
};
const isSaved = flows.some((flow) =>
Object.values(flow).includes(data.node?.display_name!)
);
return (
<>
<div className="w-26 h-10">
@ -174,10 +185,7 @@ export default function NodeToolbarComponent({
<div
data-testid="more-options-modal"
className={classNames(
"relative -ml-px inline-flex h-8 w-[31px] items-center rounded-r-md bg-background text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" +
(nodeLength == 0
? " text-muted-foreground"
: " text-foreground")
"relative -ml-px inline-flex h-8 w-[31px] items-center rounded-r-md bg-background text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
)}
>
<IconComponent
@ -191,7 +199,7 @@ export default function NodeToolbarComponent({
<SelectContent>
{nodeLength > 0 && (
<SelectItem value={nodeLength === 0 ? "disabled" : "advanced"}>
<div className="flex">
<div className="flex" data-testid="edit-button-modal">
<IconComponent
name="Settings2"
className="relative top-0.5 mr-2 h-4 w-4"
@ -201,16 +209,29 @@ export default function NodeToolbarComponent({
</SelectItem>
)}
<SelectItem value={"SaveAll"}>
<div className="flex">
<IconComponent
name="SaveAll"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Save{" "}
</div>{" "}
</SelectItem>
<SelectItem value={"Share"}>
{isSaved ? (
<SelectItem value={"override"}>
<div className="flex">
<IconComponent
name="SaveAll"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Save{" "}
</div>{" "}
</SelectItem>
) : (
<SelectItem value={"SaveAll"}>
<div className="flex">
<IconComponent
name="SaveAll"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Save{" "}
</div>{" "}
</SelectItem>
)}
<SelectItem disabled={!hasApiKey || !validApiKey} value={"Share"}>
<div className="flex">
<IconComponent
name="Share2"
@ -252,6 +273,33 @@ export default function NodeToolbarComponent({
)}
</SelectContent>
</Select>
<ConfirmationModal
asChild
open={showOverrideModal}
title={`Replace ${data.node?.display_name}`}
titleHeader={`Please, confirm your save actions`}
modalContentTitle="Attention!"
cancelText="New"
confirmationText="Replace"
icon={"SaveAll"}
index={6}
onConfirm={(index, user) => {
saveComponent(cloneDeep(data), true);
}}
onClose={setShowOverrideModal}
onCancel={() => saveComponent(cloneDeep(data), false)}
>
<ConfirmationModal.Content>
<span>
It seems {data.node?.display_name} already exists. Replacing it
will switch the current component. Proceed with replacement?
</span>
</ConfirmationModal.Content>
<ConfirmationModal.Trigger>
<></>
</ConfirmationModal.Trigger>
</ConfirmationModal>
<EditNodeModal
data={data}
nodeLength={nodeLength}

View file

@ -275,8 +275,18 @@ export default function StorePage(): JSX.Element {
<SelectContent>
<SelectGroup>
<SelectItem value="all">All</SelectItem>
<SelectItem value="createdbyme">Created By Me</SelectItem>
<SelectItem value="likedbyme">Liked By Me</SelectItem>
<SelectItem
disabled={!hasApiKey || !validApiKey}
value="createdbyme"
>
Created By Me
</SelectItem>
<SelectItem
disabled={!hasApiKey || !validApiKey}
value="likedbyme"
>
Liked By Me
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>

View file

@ -114,7 +114,7 @@
@apply pointer-events-none;
}
.extra-side-bar-buttons {
@apply relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out;
@apply relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out;
}
.extra-side-bar-buttons:hover {
@apply hover:bg-muted;

View file

@ -36,6 +36,7 @@ export type DropDownComponentType = {
editNode?: boolean;
apiModal?: boolean;
numberOfOptions?: number;
id?: string;
};
export type ParameterComponentType = {
data: NodeDataType;
@ -67,6 +68,7 @@ export type KeyPairListComponentType = {
disabled: boolean;
editNode?: boolean;
duplicateKey?: boolean;
editNodeModal?: boolean;
};
export type DictComponentType = {
@ -74,6 +76,7 @@ export type DictComponentType = {
onChange: (value) => void;
disabled: boolean;
editNode?: boolean;
id?: string;
};
export type TextAreaComponentType = {
@ -214,6 +217,7 @@ export type IconComponentProps = {
iconColor?: string;
onClick?: () => void;
stroke?: string;
id?: string;
};
export type InputProps = {
@ -277,6 +281,7 @@ export type PaginatorComponentType = {
};
export type ConfirmationModalType = {
onCancel?: () => void;
title: string;
titleHeader: string;
asChild?: boolean;

View file

@ -10,7 +10,8 @@ export type FlowsContextType = {
removeFlow: (id: string) => void;
addFlow: (
newProject: boolean,
flow?: FlowType
flow?: FlowType,
override?: boolean
) => Promise<String | undefined>;
updateFlow: (newFlow: FlowType) => void;
incrementNodeId: () => string;
@ -44,7 +45,10 @@ export type FlowsContextType = {
setLastCopiedSelection: (selection: { nodes: any; edges: any }) => void;
setTweak: (tweak: tweakType) => tweakType | void;
getTweak: tweakType;
saveComponent: (component: NodeDataType) => Promise<String | undefined>;
saveComponent: (
component: NodeDataType,
override: boolean
) => Promise<String | undefined>;
deleteComponent: (key: string) => void;
version: string;
};

View file

@ -9,7 +9,10 @@ import {
XYPosition,
} from "reactflow";
import ShortUniqueId from "short-unique-id";
import { specialCharsRegex } from "../constants/constants";
import {
LANGFLOW_SUPPORTED_TYPES,
specialCharsRegex,
} from "../constants/constants";
import { APITemplateType, TemplateVariableType } from "../types/api";
import {
FlowType,
@ -634,15 +637,7 @@ function updateGroupNodeTemplate(template: APITemplateType) {
let type = template[key].type;
let input_types = template[key].input_types;
if (
(type === "str" ||
type === "bool" ||
type === "float" ||
type === "code" ||
type === "prompt" ||
type === "file" ||
type === "int" ||
type === "dict" ||
type === "NestedDict") &&
LANGFLOW_SUPPORTED_TYPES.has(type) &&
!template[key].required &&
!input_types
) {

View file

@ -67,6 +67,7 @@ import {
Pencil,
Plus,
Redo,
RefreshCcw,
Rocket,
Save,
SaveAll,
@ -195,7 +196,7 @@ export const nodeNames: { [char: string]: string } = {
agents: "Agents",
tools: "Tools",
memories: "Memories",
saved_components: "Saved Components",
saved_components: "Saved",
advanced: "Advanced",
chat: "Chat",
embeddings: "Embeddings",
@ -354,4 +355,5 @@ export const nodeIconsLucide: iconsType = {
Heart,
Link,
ToyBrick,
RefreshCcw,
};

View file

@ -13,7 +13,7 @@ test("CodeAreaModalComponent", async ({ page }) => {
await page.waitForTimeout(2000);
await page
.locator('//*[@id="sidePythonFunctionTool"]')
.getByTestId("toolsPythonFunctionTool")
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
@ -34,8 +34,9 @@ test("CodeAreaModalComponent", async ({ page }) => {
await page
.locator('//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div')
.click();
await page.locator('//*[@id="advancedIcon"]').click();
await page.locator('//*[@id="editAdvancedBtn"]').click();
await page.getByTestId("genericModalBtnSave").click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showcode"]').click();
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeFalsy();
@ -114,8 +115,8 @@ test("CodeAreaModalComponent", async ({ page }) => {
await page
.locator('//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div')
.click();
await page.locator('//*[@id="advancedIcon"]').click();
await page.locator('//*[@id="editAdvancedBtn"]').click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showcode"]').click();
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();

View file

@ -0,0 +1,104 @@
import { expect, test } from "@playwright/test";
test("dropDownComponent", async ({ page }) => {
await page.goto("http://localhost:3000/");
await page.waitForTimeout(2000);
await page.locator('//*[@id="new-project-btn"]').click();
await page.waitForTimeout(2000);
await page.getByPlaceholder("Search").click();
await page.getByPlaceholder("Search").fill("amazon");
await page.waitForTimeout(2000);
await page
.getByTestId("llmsAmazon Bedrock")
.first()
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
await page.getByTestId("dropdown-2-display").click();
await page.getByTestId("ai21.j2-grande-instruct-0-option").click();
let value = await page.getByTestId("dropdown-2-display").innerText();
if (value !== "ai21.j2-grande-instruct") {
expect(false).toBeTruthy();
}
await page.getByTestId("dropdown-2-display").click();
await page.getByTestId("ai21.j2-jumbo-instruct-1-option").click();
value = await page.getByTestId("dropdown-2-display").innerText();
if (value !== "ai21.j2-jumbo-instruct") {
expect(false).toBeTruthy();
}
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
value = await page.getByTestId("dropdown-edit-1-display").innerText();
if (value !== "ai21.j2-jumbo-instruct") {
expect(false).toBeTruthy();
}
// showcredentials_profile_name
await page.locator('//*[@id="showcredentials_profile_name"]').click();
expect(
await page.locator('//*[@id="showcredentials_profile_name"]').isChecked()
).toBeFalsy();
// showmodel_id
await page.locator('//*[@id="showmodel_id"]').click();
expect(await page.locator('//*[@id="showmodel_id"]').isChecked()).toBeFalsy();
// showcredentials_profile_name
await page.locator('//*[@id="showcredentials_profile_name"]').click();
expect(
await page.locator('//*[@id="showcredentials_profile_name"]').isChecked()
).toBeTruthy();
// showmodel_id
await page.locator('//*[@id="showmodel_id"]').click();
expect(
await page.locator('//*[@id="showmodel_id"]').isChecked()
).toBeTruthy();
// showcredentials_profile_name
await page.locator('//*[@id="showcredentials_profile_name"]').click();
expect(
await page.locator('//*[@id="showcredentials_profile_name"]').isChecked()
).toBeFalsy();
// showmodel_id
await page.locator('//*[@id="showmodel_id"]').click();
expect(await page.locator('//*[@id="showmodel_id"]').isChecked()).toBeFalsy();
// showcredentials_profile_name
await page.locator('//*[@id="showcredentials_profile_name"]').click();
expect(
await page.locator('//*[@id="showcredentials_profile_name"]').isChecked()
).toBeTruthy();
// showmodel_id
await page.locator('//*[@id="showmodel_id"]').click();
expect(
await page.locator('//*[@id="showmodel_id"]').isChecked()
).toBeTruthy();
await page.getByTestId("dropdown-edit-1-display").click();
await page.getByTestId("ai21.j2-ultra-v1-5-option").click();
value = await page.getByTestId("dropdown-edit-1-display").innerText();
if (value !== "ai21.j2-ultra-v1") {
expect(false).toBeTruthy();
}
await page.locator('//*[@id="saveChangesBtn"]').click();
value = await page.getByTestId("dropdown-2-display").innerText();
if (value !== "ai21.j2-ultra-v1") {
expect(false).toBeTruthy();
}
});

View file

@ -42,6 +42,7 @@ test("FloatComponent", async ({ page }) => {
)
.click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showcache"]').click();
expect(await page.locator('//*[@id="showcache"]').isChecked()).toBeTruthy();
@ -385,7 +386,8 @@ test("FloatComponent", async ({ page }) => {
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div'
)
.click();
await page.locator('//*[@id="editAdvancedIcon"]').click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
// showtemperature
await page.locator('//*[@id="showtemperature"]').click();

View file

@ -14,7 +14,7 @@ test.describe("Flow Page tests", () => {
.filter({ hasText: /^Custom Component$/ })
.nth(4)
.dragTo(page.locator(".react-flow__pane"));
await page.locator("div:nth-child(4) > .extra-side-bar-buttons").click();
await page.locator(".success-alert").click();
await page.getByTestId("icon-ExternalLink").click();
await page.locator('//*[@id="checkAndSaveBtn"]').click();
});
});

View file

@ -13,7 +13,8 @@ test("IntComponent", async ({ page }) => {
await page.waitForTimeout(2000);
await page
.locator('//*[@id="sideGET Request"]')
.getByTestId("utilitiesGET Request")
.first()
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
@ -30,7 +31,7 @@ test("IntComponent", async ({ page }) => {
}
await page.locator('//*[@id="int-input-2"]').click();
await page.locator('//*[@id="int-input-2"]').fill("-3");
await page.locator('//*[@id="int-input-2"]').fill("0");
value = await page.locator('//*[@id="int-input-2"]').inputValue();
@ -41,8 +42,8 @@ test("IntComponent", async ({ page }) => {
await page
.locator('//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div')
.click();
await page.locator('//*[@id="advancedIcon"]').click();
await page.locator('//*[@id="editAdvancedBtn"]').click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
value = await page.locator('//*[@id="int-input-1"]').inputValue();
@ -80,8 +81,8 @@ test("IntComponent", async ({ page }) => {
await page
.locator('//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div')
.click();
await page.locator('//*[@id="advancedIcon"]').click();
await page.locator('//*[@id="editAdvancedBtn"]').click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showtimeout"]').click();
expect(

View file

@ -13,7 +13,7 @@ test("KeypairListComponent", async ({ page }) => {
await page.waitForTimeout(2000);
await page
.locator('//*[@id="sideCSVLoader"]')
.getByTestId("documentloadersCSVLoader")
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
@ -66,8 +66,8 @@ test("KeypairListComponent", async ({ page }) => {
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div'
)
.click();
await page.locator('//*[@id="advancedIcon"]').click();
await page.locator('//*[@id="editAdvancedBtn"]').click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showfile_path"]').click();
expect(
@ -87,8 +87,8 @@ test("KeypairListComponent", async ({ page }) => {
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div'
)
.click();
await page.locator('//*[@id="advancedIcon"]').click();
await page.locator('//*[@id="editAdvancedBtn"]').click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showfile_path"]').click();
expect(
@ -99,11 +99,11 @@ test("KeypairListComponent", async ({ page }) => {
await page.locator('//*[@id="showmetadata"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="keypair0"]').click();
await page.locator('//*[@id="keypair0"]').fill("testtesttesttest");
await page.locator('//*[@id="keypair100"]').click();
await page.locator('//*[@id="editNodekeypair0"]').click();
await page.locator('//*[@id="editNodekeypair0"]').fill("testtesttesttest");
await page.locator('//*[@id="editNodekeypair100"]').click();
await page
.locator('//*[@id="keypair100"]')
.locator('//*[@id="editNodekeypair100"]')
.fill("test test test test test test");
const plusButtonLocator = page.locator('//*[@id="plusbtn0"]');
@ -112,24 +112,24 @@ test("KeypairListComponent", async ({ page }) => {
await plusButtonLocator.click();
}
await page.locator('//*[@id="keypair1"]').click();
await page.locator('//*[@id="keypair1"]').fill("testtesttesttest1");
await page.locator('//*[@id="keypair101"]').click();
await page.locator('//*[@id="editNodekeypair1"]').click();
await page.locator('//*[@id="editNodekeypair1"]').fill("testtesttesttest1");
await page.locator('//*[@id="editNodekeypair101"]').first().click();
await page
.locator('//*[@id="keypair101"]')
.locator('//*[@id="editNodekeypair101"]')
.fill("testtesttesttesttesttest1");
await page.locator('//*[@id="plusbtn1"]').click();
await page.locator('//*[@id="editNodeplusbtn1"]').click();
await page.locator('//*[@id="keypair2"]').click();
await page.locator('//*[@id="keypair2"]').fill("testtesttesttest2");
await page.locator('//*[@id="keypair102"]').click();
await page.locator('//*[@id="editNodekeypair2"]').click();
await page.locator('//*[@id="editNodekeypair2"]').fill("testtesttesttest2");
await page.locator('//*[@id="editNodekeypair102"]').click();
await page
.locator('//*[@id="keypair102"]')
.locator('//*[@id="editNodekeypair102"]')
.fill("testtesttesttesttesttest2");
await page.locator('//*[@id="minusbtn1"]').click();
await page.locator('//*[@id="editNodeminusbtn1"]').click();
const keyPairVerification = page.locator('//*[@id="keypair102"]');
const keyPairVerification = page.locator('//*[@id="editNodekeypair102"]');
const elementKeyCount = await keyPairVerification.count();
if (elementKeyCount === 0) {
@ -143,8 +143,8 @@ test("KeypairListComponent", async ({ page }) => {
if (
key1 === "testtesttesttest" &&
value1 === "test test test test test test" &&
key2 === "testtesttesttest2" &&
value2 === "testtesttesttesttesttest2"
key2 === "testtesttesttest1" &&
value2 === "testtesttesttesttesttest1"
) {
expect(true).toBeTruthy();
} else {

View file

@ -0,0 +1,239 @@
import { expect, test } from "@playwright/test";
test("NestedComponent", async ({ page }) => {
await page.goto("http://localhost:3000/");
await page.waitForTimeout(2000);
await page.locator('//*[@id="new-project-btn"]').click();
await page.waitForTimeout(2000);
await page.getByPlaceholder("Search").click();
await page.getByPlaceholder("Search").fill("pinecone");
await page.waitForTimeout(2000);
await page
.getByTestId("vectorstoresPinecone")
.first()
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
// showindex_name
await page.locator('//*[@id="showindex_name"]').click();
expect(
await page.locator('//*[@id="showindex_name"]').isChecked()
).toBeFalsy();
// shownamespace
await page.locator('//*[@id="shownamespace"]').click();
expect(
await page.locator('//*[@id="shownamespace"]').isChecked()
).toBeFalsy();
// showpinecone_api_key
await page.locator('//*[@id="showpinecone_api_key"]').click();
expect(
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
).toBeTruthy();
// showpinecone_env
await page.locator('//*[@id="showpinecone_env"]').click();
expect(
await page.locator('//*[@id="showpinecone_env"]').isChecked()
).toBeTruthy();
// showsearch_kwargs
await page.locator('//*[@id="showsearch_kwargs"]').click();
expect(
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
).toBeTruthy();
// showindex_name
await page.locator('//*[@id="showindex_name"]').click();
expect(
await page.locator('//*[@id="showindex_name"]').isChecked()
).toBeTruthy();
// shownamespace
await page.locator('//*[@id="shownamespace"]').click();
expect(
await page.locator('//*[@id="shownamespace"]').isChecked()
).toBeTruthy();
// showpinecone_api_key
await page.locator('//*[@id="showpinecone_api_key"]').click();
expect(
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
).toBeFalsy();
// showpinecone_env
await page.locator('//*[@id="showpinecone_env"]').click();
expect(
await page.locator('//*[@id="showpinecone_env"]').isChecked()
).toBeFalsy();
// showsearch_kwargs
await page.locator('//*[@id="showsearch_kwargs"]').click();
expect(
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
).toBeFalsy();
// showindex_name
await page.locator('//*[@id="showindex_name"]').click();
expect(
await page.locator('//*[@id="showindex_name"]').isChecked()
).toBeFalsy();
// shownamespace
await page.locator('//*[@id="shownamespace"]').click();
expect(
await page.locator('//*[@id="shownamespace"]').isChecked()
).toBeFalsy();
// showpinecone_api_key
await page.locator('//*[@id="showpinecone_api_key"]').click();
expect(
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
).toBeTruthy();
// showpinecone_env
await page.locator('//*[@id="showpinecone_env"]').click();
expect(
await page.locator('//*[@id="showpinecone_env"]').isChecked()
).toBeTruthy();
// showsearch_kwargs
await page.locator('//*[@id="showsearch_kwargs"]').click();
expect(
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
).toBeTruthy();
// showindex_name
await page.locator('//*[@id="showindex_name"]').click();
expect(
await page.locator('//*[@id="showindex_name"]').isChecked()
).toBeTruthy();
// shownamespace
await page.locator('//*[@id="shownamespace"]').click();
expect(
await page.locator('//*[@id="shownamespace"]').isChecked()
).toBeTruthy();
// showpinecone_api_key
await page.locator('//*[@id="showpinecone_api_key"]').click();
expect(
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
).toBeFalsy();
// showpinecone_env
await page.locator('//*[@id="showpinecone_env"]').click();
expect(
await page.locator('//*[@id="showpinecone_env"]').isChecked()
).toBeFalsy();
// showsearch_kwargs
await page.locator('//*[@id="showsearch_kwargs"]').click();
expect(
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
).toBeFalsy();
// showindex_name
await page.locator('//*[@id="showindex_name"]').click();
expect(
await page.locator('//*[@id="showindex_name"]').isChecked()
).toBeFalsy();
// shownamespace
await page.locator('//*[@id="shownamespace"]').click();
expect(
await page.locator('//*[@id="shownamespace"]').isChecked()
).toBeFalsy();
// showpinecone_api_key
await page.locator('//*[@id="showpinecone_api_key"]').click();
expect(
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
).toBeTruthy();
// showpinecone_env
await page.locator('//*[@id="showpinecone_env"]').click();
expect(
await page.locator('//*[@id="showpinecone_env"]').isChecked()
).toBeTruthy();
// showsearch_kwargs
await page.locator('//*[@id="showsearch_kwargs"]').click();
expect(
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
).toBeTruthy();
// showindex_name
await page.locator('//*[@id="showindex_name"]').click();
expect(
await page.locator('//*[@id="showindex_name"]').isChecked()
).toBeTruthy();
// shownamespace
await page.locator('//*[@id="shownamespace"]').click();
expect(
await page.locator('//*[@id="shownamespace"]').isChecked()
).toBeTruthy();
// showpinecone_api_key
await page.locator('//*[@id="showpinecone_api_key"]').click();
expect(
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
).toBeFalsy();
// showpinecone_env
await page.locator('//*[@id="showpinecone_env"]').click();
expect(
await page.locator('//*[@id="showpinecone_env"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="saveChangesBtn"]').click();
await page.getByTestId("div-dict-input").click();
const spanElement = await page
.locator('//*[@id="radix-:r4i:"]/div[2]/div/div/code/div/div/span[1]')
.innerHTML();
if (spanElement !== "yourkey") {
expect(true).toBeFalsy();
}
});

View file

@ -13,19 +13,20 @@ test("PromptTemplateComponent", async ({ page }) => {
await page.waitForTimeout(2000);
await page
.locator('//*[@id="sidePromptTemplate"]')
.locator('//*[@id="promptsPromptTemplate"]')
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
await page.locator('//*[@id="prompt-input-4"]').click();
await page
.locator('//*[@id="modal-prompt-input-4"]')
.fill("{prompt} example {prompt1}");
await page.getByTestId("prompt-input-4-ExternalLink").click();
// await page.getByTestId("edit-prompt-sanitized").click();
let value = await page
.locator('//*[@id="modal-prompt-input-4"]')
.inputValue();
await page
.getByTestId("modal-prompt-input-4")
.fill("{prompt} example {prompt1}");
// await page.getByTestId("edit-prompt-sanitized").click();
let value = await page.getByTestId("modal-prompt-input-4").inputValue();
if (value != "{prompt} example {prompt1}") {
expect(false).toBeTruthy();
@ -43,33 +44,38 @@ test("PromptTemplateComponent", async ({ page }) => {
await page.locator('//*[@id="genericModalBtnSave"]').click();
await page.locator('//*[@id="textarea-7"]').click();
await page.locator('//*[@id="textarea-7"]').fill("prompt_value_!@#!@#");
await page.getByTestId("textarea-4-ExternalLink").click();
await page.getByTestId("text-area-modal").fill("prompt_value_!@#!@#");
value = await page.locator('//*[@id="textarea-7"]').inputValue();
value = await page.getByTestId("text-area-modal").inputValue();
if (value != "prompt_value_!@#!@#") {
expect(false).toBeTruthy();
}
await page.locator('//*[@id="textarea-8"]').click();
await page.getByTestId("genericModalBtnSave").click();
await page.getByTestId("textarea-5-ExternalLink").click();
await page
.locator('//*[@id="textarea-8"]')
.getByTestId("text-area-modal")
.fill("prompt_name_test_123123!@#!@#");
value = await page.locator('//*[@id="textarea-8"]').inputValue();
value = await page.getByTestId("text-area-modal").inputValue();
if (value != "prompt_name_test_123123!@#!@#") {
expect(false).toBeTruthy();
}
value = await page.locator('//*[@id="prompt-input-4"]').innerText();
value = await page.getByTestId("text-area-modal").inputValue();
if (value != "{prompt} example {prompt1}") {
if (value != "prompt_name_test_123123!@#!@#") {
expect(false).toBeTruthy();
}
await page.locator('//*[@id="editAdvancedIcon"]').click();
await page.getByTestId("genericModalBtnSave").click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
value = await page.locator('//*[@id="textarea-edit-1"]').inputValue();
@ -146,7 +152,8 @@ test("PromptTemplateComponent", async ({ page }) => {
)
.click();
await page.locator('//*[@id="editAdvancedIcon"]').click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showprompt1"]').click();
expect(

View file

@ -23,7 +23,7 @@ test("InputComponent", async ({ page }) => {
await page.waitForTimeout(2000);
await page
.locator('//*[@id="sideChroma"]')
.getByTestId("vectorstoresChroma")
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
@ -44,7 +44,8 @@ test("InputComponent", async ({ page }) => {
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div'
)
.click();
await page.locator('//*[@id="editAdvancedIcon"]').click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showchroma_server_cors_allow_origins"]').click();
expect(
@ -154,7 +155,8 @@ test("InputComponent", async ({ page }) => {
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div'
)
.click();
await page.locator('//*[@id="editAdvancedIcon"]').click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showcollection_name"]').click();
expect(

View file

@ -22,7 +22,7 @@ test("ToggleComponent", async ({ page }) => {
await page.waitForTimeout(2000);
await page
.locator('//*[@id="sideDirectoryLoader"]')
.getByTestId("documentloadersDirectoryLoader")
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
@ -32,8 +32,8 @@ test("ToggleComponent", async ({ page }) => {
'//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[1]/div'
)
.click();
await page.locator('//*[@id="advancedIcon"]').click();
await page.locator('//*[@id="editAdvancedBtn"]').click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showload_hidden"]').click();
expect(
@ -62,8 +62,8 @@ test("ToggleComponent", async ({ page }) => {
'//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[1]/div'
)
.click();
await page.locator('//*[@id="advancedIcon"]').click();
await page.locator('//*[@id="editAdvancedBtn"]').click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
expect(
await page.locator('//*[@id="toggle-edit-1"]').isChecked()
@ -144,8 +144,8 @@ test("ToggleComponent", async ({ page }) => {
await page
.locator('//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div')
.click();
await page.locator('//*[@id="advancedIcon"]').click();
await page.locator('//*[@id="editAdvancedBtn"]').click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showload_hidden"]').click();
expect(